Compare commits
520 Commits
v0.6.0-alp
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bbb1edd79 | ||
|
|
a2adf2b517 | ||
|
|
4deda0e155 | ||
| 4df2d3c5f8 | |||
|
|
4965165ad5 | ||
|
|
68eab3479a | ||
|
|
2a1102fc55 | ||
|
|
02b41133f8 | ||
|
|
d08780fc14 | ||
|
|
b89cc7f398 | ||
|
|
42568b9709 | ||
|
|
87f58e0a55 | ||
|
|
a61a1bef55 | ||
|
|
a99e877b7c | ||
|
|
93e9c51204 | ||
|
|
015b5bda14 | ||
|
|
2b47861a4f | ||
|
|
9b5a07191b | ||
|
|
071b6e7e23 | ||
|
|
31ec216a95 | ||
|
|
ea70d89291 | ||
|
|
03410ced6d | ||
|
|
825726c883 | ||
|
|
54e63d88d4 | ||
|
|
4018c97cb6 | ||
|
|
d0b029f995 | ||
|
|
aabe6d11b2 | ||
|
|
181cc37744 | ||
|
|
cd78f5c02c | ||
|
|
441c469966 | ||
|
|
f3c423d77d | ||
|
|
beb497878b | ||
|
|
6ea95d487c | ||
|
|
80a381dbb1 | ||
|
|
59e36491a5 | ||
|
|
c1326ba06e | ||
|
|
e2633126ee | ||
|
|
5f33435f3c | ||
|
|
54e0ff230b | ||
|
|
4e9e6233ff | ||
|
|
0056849b51 | ||
|
|
2ddb21ec5f | ||
|
|
a4eb65ca3f | ||
|
|
3002d250a1 | ||
|
|
9598553e5d | ||
|
|
7f68d9887b | ||
|
|
e092a7301f | ||
|
|
6dfdfbe514 | ||
|
|
7860f2717f | ||
|
|
60f19df9a0 | ||
|
|
951f0cda0b | ||
|
|
227042d6b1 | ||
|
|
b2c4f550ee | ||
|
|
4ddbefa88f | ||
|
|
d935115591 | ||
|
|
bd2ea42732 | ||
|
|
3bac38ee34 | ||
|
|
a7a4f6d8f2 | ||
|
|
80b6facbd3 | ||
|
|
7dee898400 | ||
|
|
4c4b2dbe17 | ||
|
|
73ed5ae697 | ||
|
|
833320de64 | ||
|
|
0d6ff8ef8a | ||
|
|
55a207a275 | ||
|
|
f33d1f1cc4 | ||
|
|
19ca2b12fc | ||
|
|
b7d3394464 | ||
|
|
8c33100d7a | ||
|
|
80c406f196 | ||
|
|
bc2c76e5e2 | ||
|
|
4a7bddbc09 | ||
|
|
e316fdbaef | ||
|
|
34106f470f | ||
|
|
d9ab0e7de8 | ||
|
|
5ca1573e2e | ||
|
|
c9ce996626 | ||
|
|
73097075d4 | ||
|
|
5e1cab2e75 | ||
|
|
30f439caec | ||
|
|
4b5db4e5a9 | ||
|
|
3fa1d6b09c | ||
|
|
6051a5f1f1 | ||
|
|
5f7dd45361 | ||
|
|
3ca975d81d | ||
|
|
7e2bab921a | ||
|
|
a7b45ee859 | ||
|
|
62f4a1f3c2 | ||
|
|
3d4e387d35 | ||
|
|
2c46640105 | ||
|
|
41443d3989 | ||
|
|
4b3470d30b | ||
|
|
ce71a3df4d | ||
|
|
7232721b87 | ||
|
|
b2f133e6f1 | ||
|
|
87920964d7 | ||
|
|
c7a4f892e3 | ||
|
|
8f9e307930 | ||
|
|
856d43ce69 | ||
|
|
26181a5a1f | ||
|
|
edf87bf9a3 | ||
|
|
5f51538e0b | ||
|
|
48bb760739 | ||
|
|
42121a9fc9 | ||
|
|
02d79a4749 | ||
|
|
ddb26c73bd | ||
|
|
8ff064fe28 | ||
|
|
cf25eb0833 | ||
|
|
5c4260f3ac | ||
|
|
7abf19804c | ||
|
|
df707d5bef | ||
|
|
f3b0b0027e | ||
|
|
106a01223e | ||
|
|
506a60d0be | ||
|
|
4018607b77 | ||
|
|
1cc720ad34 | ||
|
|
73828af715 | ||
|
|
c0a96e3811 | ||
|
|
9d0d76f0a5 | ||
|
|
c7173ac832 | ||
|
|
b12ce47e7e | ||
|
|
269272983b | ||
|
|
6adc5eb9fb | ||
|
|
fd8bc8ae3f | ||
|
|
3369b0d429 | ||
|
|
097d39e8ce | ||
|
|
11fa08e6d6 | ||
|
|
96987af71d | ||
|
|
23327cb3ef | ||
|
|
b43b45be8f | ||
|
|
41994ec82e | ||
|
|
cd14ea7c62 | ||
|
|
9f13bca6b8 | ||
|
|
f4252c3e97 | ||
|
|
6598867d3d | ||
|
|
f39e040a0d | ||
|
|
a3d140269b | ||
|
|
feac29ea40 | ||
|
|
834c8cdbca | ||
|
|
d441fbdf91 | ||
|
|
e897f524dc | ||
|
|
1caf9446d8 | ||
|
|
bfd2c885db | ||
|
|
f467fa4e59 | ||
|
|
41fca4a2bb | ||
|
|
d38f955144 | ||
|
|
010e50a25d | ||
|
|
eab7c607cd | ||
|
|
3cafbf7f1a | ||
|
|
d7277b5a5b | ||
|
|
bb8dd1402d | ||
|
|
539cd0d418 | ||
|
|
0f8e1625d5 | ||
|
|
840e9bedb2 | ||
|
|
e11ad476fc | ||
|
|
c43726bc75 | ||
|
|
c9ab933a23 | ||
|
|
066a0b1668 | ||
|
|
aa68fe412e | ||
|
|
d748ea7e40 | ||
|
|
a7a282078f | ||
|
|
2fad3b588a | ||
|
|
4cb5d4a9c5 | ||
|
|
5b22ef192e | ||
|
|
34024d8524 | ||
|
|
ecc31dea15 | ||
|
|
d0e48d9bdc | ||
|
|
f6fc5599c0 | ||
|
|
89b6666436 | ||
|
|
94d45a2262 | ||
|
|
dc17a185ca | ||
|
|
4915477823 | ||
|
|
8662d2227c | ||
|
|
dd01a9bef3 | ||
|
|
6e320b1fd5 | ||
|
|
6e40993199 | ||
|
|
9d48af2b50 | ||
|
|
28d48a76e3 | ||
|
|
80eb14f9da | ||
|
|
623fa06d52 | ||
|
|
7154113c13 | ||
|
|
0a932ff880 | ||
|
|
7ef4a1ff12 | ||
|
|
ccc52b69d2 | ||
|
|
8139fdc738 | ||
|
|
a4fd53d516 | ||
|
|
27357110b5 | ||
|
|
fde78738b6 | ||
|
|
ce530e795a | ||
|
|
658d211779 | ||
|
|
66c1cd0d93 | ||
|
|
55ac231cec | ||
|
|
67674d53a2 | ||
|
|
8646b2dfce | ||
|
|
a2f68c2e3c | ||
|
|
5b1a090c5e | ||
|
|
e9efa238b0 | ||
|
|
0dabd8a1f6 | ||
|
|
b7a1f82f56 | ||
|
|
2507e096b7 | ||
|
|
b303b0a99c | ||
|
|
342fb57d14 | ||
|
|
a9ca77e27f | ||
|
|
6542304340 | ||
|
|
6b0f7f94b6 | ||
|
|
d87c76a23e | ||
|
|
305423b856 | ||
|
|
4b62190aff | ||
|
|
e17771a6a5 | ||
|
|
bac30ae78b | ||
|
|
c0219922b2 | ||
|
|
b5ed952d5c | ||
|
|
7994bc4909 | ||
|
|
b88d34c639 | ||
|
|
96cb1ee3fd | ||
|
|
003abe86bb | ||
|
|
6ec411a618 | ||
|
|
c7c0903804 | ||
|
|
cdc972a5ce | ||
|
|
17de912608 | ||
|
|
9876508887 | ||
|
|
72d62220e8 | ||
|
|
46ad337fa5 | ||
|
|
7a3ba7721f | ||
|
|
e0198e626b | ||
|
|
142705f386 | ||
|
|
4cb49c7825 | ||
|
|
05adb82dda | ||
|
|
faf7671799 | ||
|
|
d64db329dd | ||
|
|
e34d2339ad | ||
|
|
d196c5091c | ||
|
|
3e83f92532 | ||
|
|
41d7aaf323 | ||
|
|
e110f3726a | ||
|
|
eb5c897ac0 | ||
|
|
e864cf0c7d | ||
|
|
565c12405e | ||
|
|
2a6a8b42a6 | ||
|
|
5cb4cc0d1d | ||
|
|
62eb4f026f | ||
|
|
411d1a89c1 | ||
|
|
6ae0bf366a | ||
|
|
178cdc9dfa | ||
|
|
5bf1f86886 | ||
|
|
e482aba030 | ||
|
|
535e4d42bb | ||
|
|
54398d4c4b | ||
|
|
0987b44e23 | ||
|
|
58098e96d4 | ||
|
|
f649c180cf | ||
|
|
966478b131 | ||
|
|
ca0759a011 | ||
|
|
f1cdf2fe79 | ||
|
|
04fa5e2719 | ||
|
|
eccb95690c | ||
|
|
acf7456371 | ||
|
|
8ea41480b7 | ||
|
|
0d1bc0d7fe | ||
|
|
f9b7774286 | ||
|
|
2e672badf2 | ||
|
|
cd5d6fd15c | ||
|
|
cf76982cfa | ||
|
|
2624ea7d2a | ||
|
|
2e7c4b6dff | ||
|
|
e5dbee1073 | ||
|
|
c06463b61d | ||
|
|
10341e3776 | ||
|
|
824cdc5dcd | ||
|
|
7aa7a35aa5 | ||
|
|
79b57b3359 | ||
|
|
c7aeffe586 | ||
|
|
79c02f2457 | ||
|
|
b9ed68fa9e | ||
|
|
6c6d6c9c8d | ||
|
|
e169d7be68 | ||
|
|
192fd7d4dd | ||
|
|
1f1193e857 | ||
|
|
0a412ade88 | ||
|
|
3cef2fe9aa | ||
|
|
18e53f06fe | ||
|
|
eaac539087 | ||
|
|
57005c4fd5 | ||
|
|
ea58999a2a | ||
|
|
a5a69fd9fc | ||
|
|
f1d0f1c1c9 | ||
|
|
83492d7495 | ||
|
|
10abc2254a | ||
|
|
5d5536a95e | ||
|
|
fe81e55925 | ||
|
|
654690d73e | ||
|
|
256aba4bc1 | ||
|
|
49f8845105 | ||
|
|
12370846b4 | ||
|
|
07637dfe96 | ||
|
|
f389a4a1db | ||
|
|
af077c796c | ||
|
|
1c319f2fa8 | ||
|
|
e8afa03f7b | ||
|
|
9b22545842 | ||
|
|
adc733d25c | ||
|
|
6896257647 | ||
|
|
1d9845a6cb | ||
|
|
8461339e9a | ||
|
|
9904d94e4c | ||
|
|
da25c80d0b | ||
|
|
5fa63733fd | ||
|
|
8b54bf0ba1 | ||
|
|
173dc12cb3 | ||
|
|
e136529ff0 | ||
|
|
75542dacb3 | ||
|
|
07abfbde43 | ||
|
|
96112fe118 | ||
|
|
9d0b313261 | ||
|
|
277ddfa9be | ||
|
|
5d88bdb256 | ||
|
|
8d29b43155 | ||
|
|
cc071a6415 | ||
|
|
8954def25c | ||
|
|
d484098781 | ||
|
|
9f06cbf3a0 | ||
|
|
af4a3ca0af | ||
|
|
43715143e4 | ||
|
|
16aa354d36 | ||
|
|
c739025453 | ||
|
|
f0526d17de | ||
|
|
6cc2e919c0 | ||
|
|
e1f9eaefa9 | ||
|
|
5d62bf9b60 | ||
|
|
4aa5814a0a | ||
|
|
5bca244062 | ||
|
|
7cf57714a4 | ||
|
|
92e6f862ad | ||
|
|
2377f41036 | ||
|
|
26a08f4d9f | ||
|
|
672d207961 | ||
|
|
a3d8e50f26 | ||
|
|
d3abca46a8 | ||
|
|
17fdd18c55 | ||
|
|
e482d038c6 | ||
|
|
d0482a004e | ||
|
|
561a3e3256 | ||
|
|
158dac5e1c | ||
|
|
1413f8c0b6 | ||
|
|
ffb2aa332a | ||
|
|
45e8208e9c | ||
|
|
7f230a8bf4 | ||
|
|
afe26b8c16 | ||
|
|
d153f62b8a | ||
|
|
5c80cd6e50 | ||
|
|
df1aced95d | ||
|
|
5a0a8f5077 | ||
|
|
1c281539e0 | ||
|
|
ef41d14f50 | ||
|
|
aa29397732 | ||
|
|
ca8865a3ce | ||
|
|
7599ef6626 | ||
|
|
43e71f9242 | ||
|
|
aac9b25efe | ||
|
|
532d170585 | ||
|
|
3dcb14013b | ||
|
|
a4c782cd35 | ||
|
|
0779baa0ac | ||
|
|
0849fab644 | ||
|
|
d85bca2e7e | ||
|
|
c7ab6c435c | ||
|
|
98db3b4f08 | ||
|
|
0e7ba839ed | ||
|
|
2c27691e57 | ||
|
|
934506bdfd | ||
|
|
f313fa4ae1 | ||
|
|
78755b6130 | ||
|
|
8bf59e5cbb | ||
|
|
8f71d13714 | ||
|
|
f4a790eb50 | ||
|
|
0d951c8ad1 | ||
|
|
59ef5f05ea | ||
|
|
b84aab0960 | ||
|
|
0e89b91c38 | ||
|
|
7888a704e1 | ||
|
|
804fd3de8e | ||
|
|
4992c36f08 | ||
|
|
27af0c841b | ||
|
|
cc4f4df4f9 | ||
|
|
5989ab3b85 | ||
|
|
040b9c1452 | ||
|
|
bb7bd2e27e | ||
|
|
02dbd4ba75 | ||
|
|
3b149cc875 | ||
|
|
f3745c68d2 | ||
|
|
eddbe7d0cf | ||
|
|
faf86d006a | ||
|
|
71e4dfeb14 | ||
|
|
da8ed6ddf9 | ||
|
|
503e687c55 | ||
|
|
dd667cce0b | ||
|
|
f911009752 | ||
|
|
cae4c46669 | ||
|
|
a937fc38db | ||
|
|
ff43ffce07 | ||
|
|
5bb3dc258a | ||
|
|
98238e4516 | ||
|
|
23b8308282 | ||
|
|
a1f7cdc2b6 | ||
|
|
dcb30351c5 | ||
|
|
1c0dc60071 | ||
|
|
0401c3ac0e | ||
|
|
3b800d1cd7 | ||
|
|
35efb464e5 | ||
|
|
19a955a327 | ||
|
|
836b18e68a | ||
|
|
046dc0d598 | ||
|
|
039d92ce78 | ||
|
|
2136da15d6 | ||
|
|
e5041d80ef | ||
|
|
f66860bdfe | ||
|
|
50f0a2bd2e | ||
|
|
7cd9bd383e | ||
|
|
45a20a1633 | ||
|
|
ec5b3f80ef | ||
|
|
3b257ea87a | ||
|
|
6b82cead9c | ||
|
|
79ae4c52cd | ||
|
|
a4616cda7d | ||
|
|
95648361be | ||
|
|
0c41e9c10b | ||
|
|
61c7ef3cb0 | ||
|
|
65898125d0 | ||
|
|
da05078ff3 | ||
|
|
badb5c3636 | ||
|
|
9453588ab1 | ||
|
|
4cbb3874a4 | ||
|
|
940aff7ef4 | ||
|
|
a3edb93273 | ||
|
|
782b054b1a | ||
|
|
fc27b2911e | ||
|
|
486b067567 | ||
|
|
bdd1afea57 | ||
|
|
5ccd02939b | ||
|
|
ca5b8ab309 | ||
|
|
9481fdd38d | ||
|
|
56bf6d282b | ||
|
|
5364c9c1bc | ||
|
|
a4d4beeb97 | ||
|
|
30a01381be | ||
|
|
def99a9e4d | ||
|
|
1214fc8b0d | ||
|
|
5a5b1268d3 | ||
|
|
6a1fffd1ce | ||
|
|
571ef3060a | ||
|
|
3cf82e112f | ||
|
|
9b459c20d6 | ||
|
|
5c85cd27c3 | ||
|
|
4bf73c7a8a | ||
|
|
62e51b7535 | ||
|
|
729a7f0053 | ||
|
|
03c297190d | ||
|
|
9c790eddd7 | ||
|
|
8ebe7e332b | ||
|
|
f43777e37e | ||
|
|
691a8fcdeb | ||
|
|
69c0e8d70e | ||
|
|
330c92c9a8 | ||
|
|
cf0ff60d31 | ||
|
|
9e9cf5dd1f | ||
|
|
5768d8e4a4 | ||
|
|
3b433e53be | ||
|
|
28ddf40344 | ||
|
|
c1286db9c1 | ||
|
|
f13940262e | ||
|
|
9f0a6101b8 | ||
|
|
0b0fe01a7c | ||
|
|
85e098d5c8 | ||
|
|
e8d1faf4e2 | ||
|
|
2a46df1110 | ||
|
|
1601e6d26e | ||
|
|
0e4f6dea2b | ||
|
|
a2356773dc | ||
|
|
4a4da4737d | ||
|
|
2cefd1bf2e | ||
|
|
c5ce15fe34 | ||
|
|
76dea568c9 | ||
|
|
8e81140eff | ||
|
|
d58e1f0792 | ||
|
|
830c242751 | ||
|
|
91d45afd0f | ||
|
|
102c77b23e | ||
|
|
aa4039a2e7 | ||
|
|
c065ded440 | ||
|
|
f69096dadb | ||
|
|
363f95ba14 | ||
|
|
dcc15e8911 | ||
|
|
bd13047b58 | ||
|
|
1a83789c01 | ||
|
|
1cbe272e19 | ||
|
|
6de74b41b3 | ||
|
|
b0fc9ee507 | ||
|
|
a95ffe1d31 | ||
|
|
4119a510f5 | ||
|
|
cfa7f03815 | ||
|
|
9b4c89820d | ||
|
|
fe874720aa | ||
|
|
f7c0f33eb5 | ||
|
|
c1bf9d9dfc | ||
|
|
255bf439d3 | ||
|
|
2a31937b81 | ||
|
|
eb8964e1d1 | ||
|
|
fe0ca280a1 | ||
|
|
e8e3b7b985 | ||
|
|
c437507442 | ||
|
|
ca151b434e | ||
|
|
10ba74c21e | ||
|
|
6191b6371d | ||
|
|
5be4175ac3 | ||
|
|
f61f3671a6 | ||
|
|
477e0ada32 | ||
|
|
a884f693ae | ||
|
|
3107b1b21b | ||
|
|
04529de7b3 | ||
|
|
199a39208c |
2
.dir-locals.el
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
((nil . ((eglot-luau-rojo-project-path . "plugin.project.json")
|
||||||
|
(eglot-luau-rojo-sourcemap-enabled . 't))))
|
||||||
@@ -23,4 +23,7 @@ insert_final_newline = true
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[*.lua]
|
[*.lua]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.luau]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# stylua formatting
|
||||||
|
0f8e1625d572a5fe0f7b5c08653ff92cc837d346
|
||||||
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.lua linguist-language=Luau
|
||||||
23
.github/workflows/changelog.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Changelog Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Check Actions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Changelog check
|
||||||
|
uses: Zomzog/changelog-checker@v1.3.0
|
||||||
|
with:
|
||||||
|
fileName: CHANGELOG.md
|
||||||
|
noChangelogLabel: skip changelog
|
||||||
|
checkNotification: Simple
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
131
.github/workflows/ci.yml
vendored
@@ -1,36 +1,133 @@
|
|||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on: [push]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: Build and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: [stable, "1.40.0"]
|
os: [ubuntu-22.04, windows-latest, macos-latest, windows-11-arm, ubuntu-22.04-arm]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Install Rust
|
||||||
run: rustup default ${{ matrix.rust_version }}
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Restore Rust Cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --locked --verbose
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Test
|
||||||
run: cargo test --locked --verbose
|
run: cargo test --locked --verbose
|
||||||
|
|
||||||
- name: Rustfmt and Clippy
|
- name: Save Rust Cache
|
||||||
run: |
|
uses: actions/cache/save@v4
|
||||||
cargo fmt -- --check
|
with:
|
||||||
cargo clippy
|
path: |
|
||||||
if: matrix.rust_version == 'stable'
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Build (All Features)
|
msrv:
|
||||||
run: cargo build --locked --verbose --all-features
|
name: Check MSRV
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
- name: Run tests (All Features)
|
steps:
|
||||||
run: cargo test --locked --verbose --all-features
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@1.88.0
|
||||||
|
|
||||||
|
- name: Restore Rust Cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
|
- name: Save Rust Cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Rustfmt, Clippy, Stylua, & Selene
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Restore Rust Cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Setup Rokit
|
||||||
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
version: 'v1.1.0'
|
||||||
|
|
||||||
|
- name: Stylua
|
||||||
|
run: stylua --check plugin/src
|
||||||
|
|
||||||
|
- name: Selene
|
||||||
|
run: selene plugin/src
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy
|
||||||
|
|
||||||
|
- name: Save Rust Cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|||||||
197
.github/workflows/release.yml
vendored
@@ -2,59 +2,156 @@ name: Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["*"]
|
tags: ["v*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
create-release:
|
||||||
runs-on: windows-latest
|
name: Create Release
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --verbose --locked --release
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-win64
|
|
||||||
path: target/release/rojo.exe
|
|
||||||
|
|
||||||
macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: |
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
cargo build --verbose --locked --release
|
|
||||||
env:
|
|
||||||
OPENSSL_STATIC: 1
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-macos
|
|
||||||
path: target/release/rojo
|
|
||||||
|
|
||||||
linux:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
|
- name: Create Release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh release create ${{ github.ref_name }} --draft --verify-tag --title ${{ github.ref_name }}
|
||||||
|
|
||||||
- name: Build
|
build-plugin:
|
||||||
run: cargo build --locked --verbose --release
|
needs: ["create-release"]
|
||||||
env:
|
name: Build Roblox Studio Plugin
|
||||||
OPENSSL_STATIC: 1
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Setup Rokit
|
||||||
uses: actions/upload-artifact@v1
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
with:
|
with:
|
||||||
name: rojo-linux
|
version: 'v1.1.0'
|
||||||
path: target/release/rojo
|
|
||||||
|
- name: Build Plugin
|
||||||
|
run: rojo build plugin.project.json --output Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh release upload ${{ github.ref_name }} Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Rojo.rbxm
|
||||||
|
path: Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Roblox
|
||||||
|
env:
|
||||||
|
RBX_API_KEY: ${{ secrets.PLUGIN_UPLOAD_TOKEN }}
|
||||||
|
RBX_UNIVERSE_ID: ${{ vars.PLUGIN_CI_PLACE_ID }}
|
||||||
|
RBX_PLACE_ID: ${{ vars.PLUGIN_CI_UNIVERSE_ID }}
|
||||||
|
run: lune run upload-plugin Rojo.rbxm
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: ["create-release"]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
include:
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-22.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
label: linux-x86_64
|
||||||
|
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-22.04-arm
|
||||||
|
target: aarch64-unknown-linux-gnu
|
||||||
|
label: linux-aarch64
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
label: windows-x86_64
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-11-arm
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
label: windows-aarch64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
label: macos-x86_64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
label: macos-aarch64
|
||||||
|
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
BIN: rojo
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Restore Rust Cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: cargo build --release --locked --verbose --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Save Rust Cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
target
|
||||||
|
key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Generate Artifact Name
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TAG_NAME: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
echo "ARTIFACT_NAME=$BIN-${TAG_NAME#v}-${{ matrix.label }}.zip" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Create Archive and Upload to Release
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
mkdir staging
|
||||||
|
|
||||||
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
|
cp "target/${{ matrix.target }}/release/$BIN.exe" staging/
|
||||||
|
cd staging
|
||||||
|
7z a ../$ARTIFACT_NAME *
|
||||||
|
else
|
||||||
|
cp "target/${{ matrix.target }}/release/$BIN" staging/
|
||||||
|
cd staging
|
||||||
|
zip ../$ARTIFACT_NAME *
|
||||||
|
fi
|
||||||
|
|
||||||
|
gh release upload ${{ github.ref_name }} ../$ARTIFACT_NAME
|
||||||
|
|
||||||
|
- name: Upload Archive to Artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.ARTIFACT_NAME }}
|
||||||
|
name: ${{ env.ARTIFACT_NAME }}
|
||||||
|
|||||||
12
.gitignore
vendored
@@ -10,9 +10,19 @@
|
|||||||
/*.rbxl
|
/*.rbxl
|
||||||
/*.rbxlx
|
/*.rbxlx
|
||||||
|
|
||||||
|
# Sourcemap for the Rojo plugin (for better intellisense)
|
||||||
|
/sourcemap.json
|
||||||
|
|
||||||
# Roblox Studio holds 'lock' files on places
|
# Roblox Studio holds 'lock' files on places
|
||||||
*.rbxl.lock
|
*.rbxl.lock
|
||||||
*.rbxlx.lock
|
*.rbxlx.lock
|
||||||
|
|
||||||
# Snapshot files from the 'insta' Rust crate
|
# Snapshot files from the 'insta' Rust crate
|
||||||
**/*.snap.new
|
**/*.snap.new
|
||||||
|
|
||||||
|
# Macos file system junk
|
||||||
|
._*
|
||||||
|
.DS_STORE
|
||||||
|
|
||||||
|
# JetBrains IDEs
|
||||||
|
/.idea/
|
||||||
|
|||||||
37
.gitmodules
vendored
@@ -1,15 +1,24 @@
|
|||||||
[submodule "plugin/modules/roact"]
|
[submodule "plugin/Packages/Roact"]
|
||||||
path = plugin/modules/roact
|
path = plugin/Packages/Roact
|
||||||
url = https://github.com/Roblox/roact.git
|
url = https://github.com/roblox/roact.git
|
||||||
[submodule "plugin/modules/testez"]
|
[submodule "plugin/Packages/Flipper"]
|
||||||
path = plugin/modules/testez
|
path = plugin/Packages/Flipper
|
||||||
url = https://github.com/Roblox/testez.git
|
url = https://github.com/reselim/flipper.git
|
||||||
[submodule "plugin/modules/promise"]
|
[submodule "plugin/Packages/Promise"]
|
||||||
path = plugin/modules/promise
|
path = plugin/Packages/Promise
|
||||||
url = https://github.com/LPGhatguy/roblox-lua-promise.git
|
url = https://github.com/evaera/roblox-lua-promise.git
|
||||||
[submodule "plugin/modules/t"]
|
[submodule "plugin/Packages/t"]
|
||||||
path = plugin/modules/t
|
path = plugin/Packages/t
|
||||||
url = https://github.com/osyrisrblx/t.git
|
url = https://github.com/osyrisrblx/t.git
|
||||||
[submodule "plugin/modules/rbx-dom"]
|
[submodule "plugin/Packages/TestEZ"]
|
||||||
path = plugin/modules/rbx-dom
|
path = plugin/Packages/TestEZ
|
||||||
url = http://github.com/rojo-rbx/rbx-dom
|
url = https://github.com/roblox/testez.git
|
||||||
|
[submodule "plugin/Packages/Highlighter"]
|
||||||
|
path = plugin/Packages/Highlighter
|
||||||
|
url = https://github.com/boatbomber/highlighter.git
|
||||||
|
[submodule "plugin/Packages/msgpack-luau"]
|
||||||
|
path = plugin/Packages/msgpack-luau
|
||||||
|
url = https://github.com/cipharius/msgpack-luau/
|
||||||
|
[submodule ".lune/opencloud-execute"]
|
||||||
|
path = .lune/opencloud-execute
|
||||||
|
url = https://github.com/Dekkonot/opencloud-luau-execute-lune.git
|
||||||
|
|||||||
58
.luacheckrc
@@ -1,58 +0,0 @@
|
|||||||
stds.roblox = {
|
|
||||||
read_globals = {
|
|
||||||
game = {
|
|
||||||
other_fields = true,
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Roblox globals
|
|
||||||
"script",
|
|
||||||
|
|
||||||
-- Extra functions
|
|
||||||
"tick", "warn", "spawn",
|
|
||||||
"wait", "settings", "typeof",
|
|
||||||
|
|
||||||
-- Types
|
|
||||||
"Vector2", "Vector3",
|
|
||||||
"Vector2int16", "Vector3int16",
|
|
||||||
"Color3",
|
|
||||||
"UDim", "UDim2",
|
|
||||||
"Rect",
|
|
||||||
"CFrame",
|
|
||||||
"Enum",
|
|
||||||
"Instance",
|
|
||||||
"DockWidgetPluginGuiInfo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.plugin = {
|
|
||||||
read_globals = {
|
|
||||||
"plugin",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.testez = {
|
|
||||||
read_globals = {
|
|
||||||
"describe",
|
|
||||||
"it", "itFOCUS", "itSKIP",
|
|
||||||
"FOCUS", "SKIP", "HACK_NO_XPCALL",
|
|
||||||
"expect",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ignore = {
|
|
||||||
"212", -- unused arguments
|
|
||||||
"421", -- shadowing local variable
|
|
||||||
"422", -- shadowing argument
|
|
||||||
"431", -- shadowing upvalue
|
|
||||||
"432", -- shadowing upvalue argument
|
|
||||||
}
|
|
||||||
|
|
||||||
std = "lua51+roblox"
|
|
||||||
|
|
||||||
files["**/*.server.lua"] = {
|
|
||||||
std = "+plugin",
|
|
||||||
}
|
|
||||||
|
|
||||||
files["**/*.spec.lua"] = {
|
|
||||||
std = "+testez",
|
|
||||||
}
|
|
||||||
8
.lune/.config.luau
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
return {
|
||||||
|
luau = {
|
||||||
|
languagemode = "strict",
|
||||||
|
aliases = {
|
||||||
|
lune = "~/.lune/.typedefs/0.10.4/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
1
.lune/opencloud-execute
Submodule
51
.lune/scripts/plugin-upload.luau
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
local args: any = ...
|
||||||
|
assert(args, "no arguments passed to script")
|
||||||
|
|
||||||
|
local input: buffer = args.BinaryInput
|
||||||
|
|
||||||
|
local AssetService = game:GetService("AssetService")
|
||||||
|
local SerializationService = game:GetService("SerializationService")
|
||||||
|
local EncodingService = game:GetService("EncodingService")
|
||||||
|
|
||||||
|
local input_hash: buffer = EncodingService:ComputeBufferHash(input, Enum.HashAlgorithm.Sha256)
|
||||||
|
local hex_hash: { string } = table.create(buffer.len(input_hash))
|
||||||
|
for i = 0, buffer.len(input_hash) - 1 do
|
||||||
|
table.insert(hex_hash, string.format("%02x", buffer.readu8(input_hash, i)))
|
||||||
|
end
|
||||||
|
|
||||||
|
print(`Deserializing plugin file (size: {buffer.len(input)} bytes, hash: {table.concat(hex_hash, "")})`)
|
||||||
|
local plugin = SerializationService:DeserializeInstancesAsync(input)[1]
|
||||||
|
|
||||||
|
local UploadDetails = require(plugin.UploadDetails) :: any
|
||||||
|
local PLUGIN_ID = UploadDetails.assetId
|
||||||
|
local PLUGIN_NAME = UploadDetails.name
|
||||||
|
local PLUGIN_DESCRIPTION = UploadDetails.description
|
||||||
|
local PLUGIN_CREATOR_ID = UploadDetails.creatorId
|
||||||
|
local PLUGIN_CREATOR_TYPE = UploadDetails.creatorType
|
||||||
|
|
||||||
|
assert(typeof(PLUGIN_ID) == "number", "UploadDetails did not contain a number field 'assetId'")
|
||||||
|
assert(typeof(PLUGIN_NAME) == "string", "UploadDetails did not contain a string field 'name'")
|
||||||
|
assert(typeof(PLUGIN_DESCRIPTION) == "string", "UploadDetails did not contain a string field 'description'")
|
||||||
|
assert(typeof(PLUGIN_CREATOR_ID) == "number", "UploadDetails did not contain a number field 'creatorId'")
|
||||||
|
assert(typeof(PLUGIN_CREATOR_TYPE) == "string", "UploadDetails did not contain a string field 'creatorType'")
|
||||||
|
assert(
|
||||||
|
Enum.AssetCreatorType:FromName(PLUGIN_CREATOR_TYPE) ~= nil,
|
||||||
|
"UploadDetails field 'creatorType' was not a valid member of Enum.AssetCreatorType"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(`Uploading to {PLUGIN_ID}`)
|
||||||
|
print(`Plugin Name: {PLUGIN_NAME}`)
|
||||||
|
print(`Plugin Description: {PLUGIN_DESCRIPTION}`)
|
||||||
|
|
||||||
|
local result, version_or_err = AssetService:CreateAssetVersionAsync(plugin, Enum.AssetType.Plugin, PLUGIN_ID, {
|
||||||
|
["Name"] = PLUGIN_NAME,
|
||||||
|
["Description"] = PLUGIN_DESCRIPTION,
|
||||||
|
["CreatorId"] = PLUGIN_CREATOR_ID,
|
||||||
|
["CreatorType"] = Enum.AssetCreatorType:FromName(PLUGIN_CREATOR_TYPE),
|
||||||
|
})
|
||||||
|
|
||||||
|
if result ~= Enum.CreateAssetResult.Success then
|
||||||
|
error(`Plugin failed to upload because: {result.Name} - {version_or_err}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
print(`Plugin uploaded successfully. New version is {version_or_err}.`)
|
||||||
78
.lune/upload-plugin.luau
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
local fs = require("@lune/fs")
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
|
||||||
|
local luau_execute = require("./opencloud-execute")
|
||||||
|
|
||||||
|
local UNIVERSE_ID = process.env["RBX_UNIVERSE_ID"]
|
||||||
|
local PLACE_ID = process.env["RBX_PLACE_ID"]
|
||||||
|
|
||||||
|
local version_string = fs.readFile("plugin/Version.txt")
|
||||||
|
local versions = { string.match(version_string, "^v?(%d+)%.(%d+)%.(%d+)(.*)$") }
|
||||||
|
if versions[4] ~= "" then
|
||||||
|
print("This release is a pre-release. Skipping uploading plugin.")
|
||||||
|
process.exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local plugin_path = process.args[1]
|
||||||
|
assert(
|
||||||
|
typeof(plugin_path) == "string",
|
||||||
|
"no plugin path provided, expected usage is `lune run upload-plugin [PATH TO RBXM]`."
|
||||||
|
)
|
||||||
|
|
||||||
|
-- For local testing
|
||||||
|
if process.env["CI"] ~= "true" then
|
||||||
|
local rojo = process.exec("rojo", { "build", "plugin.project.json", "--output", plugin_path })
|
||||||
|
if not rojo.ok then
|
||||||
|
stdio.ewrite("plugin upload failed because: could not build plugin.rbxm\n\n")
|
||||||
|
stdio.ewrite(rojo.stderr)
|
||||||
|
stdio.ewrite("\n")
|
||||||
|
process.exit(1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
assert(fs.isFile(plugin_path), `Plugin file did not exist at {plugin_path}`)
|
||||||
|
end
|
||||||
|
local plugin_content = fs.readFile(plugin_path)
|
||||||
|
|
||||||
|
local engine_script = fs.readFile(".lune/scripts/plugin-upload.luau")
|
||||||
|
|
||||||
|
print("Creating task to upload plugin")
|
||||||
|
local task = luau_execute.create_task_latest(UNIVERSE_ID, PLACE_ID, engine_script, 300, false, plugin_content)
|
||||||
|
|
||||||
|
print("Waiting for task to finish")
|
||||||
|
local success = luau_execute.await_finish(task)
|
||||||
|
if not success then
|
||||||
|
local error = luau_execute.get_error(task)
|
||||||
|
assert(error, "could not fetch error from task")
|
||||||
|
stdio.ewrite("plugin upload failed because: task did not finish successfully\n\n")
|
||||||
|
stdio.ewrite(error.code)
|
||||||
|
stdio.ewrite("\n")
|
||||||
|
stdio.ewrite(error.message)
|
||||||
|
stdio.ewrite("\n")
|
||||||
|
process.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Output from task:\n")
|
||||||
|
for _, log in luau_execute.get_structured_logs(task) do
|
||||||
|
if log.messageType == "ERROR" then
|
||||||
|
stdio.write(stdio.color("red"))
|
||||||
|
stdio.write(log.message)
|
||||||
|
stdio.write("\n")
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
elseif log.messageType == "INFO" then
|
||||||
|
stdio.write(stdio.color("cyan"))
|
||||||
|
stdio.write(log.message)
|
||||||
|
stdio.write("\n")
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
elseif log.messageType == "WARNING" then
|
||||||
|
stdio.write(stdio.color("yellow"))
|
||||||
|
stdio.write(log.message)
|
||||||
|
stdio.write("\n")
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
else
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
stdio.write(log.message)
|
||||||
|
stdio.write("\n")
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
end
|
||||||
|
end
|
||||||
8
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"JohnnyMorganz.luau-lsp",
|
||||||
|
"JohnnyMorganz.stylua",
|
||||||
|
"Kampfkarren.selene-vscode",
|
||||||
|
"rust-lang.rust-analyzer"
|
||||||
|
]
|
||||||
|
}
|
||||||
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"luau-lsp.sourcemap.rojoProjectFile": "plugin.project.json",
|
||||||
|
"luau-lsp.sourcemap.autogenerate": true
|
||||||
|
}
|
||||||
1285
CHANGELOG.md
@@ -1,5 +1,5 @@
|
|||||||
# Contributing to the Rojo Project
|
# Contributing to the Rojo Project
|
||||||
Rojo is a big project and can always use more help! This guide covers all repositories underneath the [rojo-rbx organization on GitHub](https://github.com/rojo-rbx).
|
Rojo is a big project and can always use more help!
|
||||||
|
|
||||||
Some of the repositories covered are:
|
Some of the repositories covered are:
|
||||||
|
|
||||||
@@ -14,14 +14,31 @@ Code contributions are welcome for features and bugs that have been reported in
|
|||||||
You'll want these tools to work on Rojo:
|
You'll want these tools to work on Rojo:
|
||||||
|
|
||||||
* Latest stable Rust compiler
|
* Latest stable Rust compiler
|
||||||
|
* Rustfmt and Clippy are used for code formatting and linting.
|
||||||
* Latest stable [Rojo](https://github.com/rojo-rbx/rojo)
|
* Latest stable [Rojo](https://github.com/rojo-rbx/rojo)
|
||||||
* Latest stable [Remodel](https://github.com/rojo-rbx/remodel)
|
* [Rokit](https://github.com/rojo-rbx/rokit)
|
||||||
* Latest stable [run-in-roblox](https://github.com/rojo-rbx/run-in-roblox)
|
* [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) (Only needed if working on the Studio plugin.)
|
||||||
|
|
||||||
|
When working on the Studio plugin, we recommend using this command to automatically rebuild the plugin when you save a change:
|
||||||
|
|
||||||
|
*(Make sure you've enabled the Studio setting to reload plugins on file change!)*
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/watch-build-plugin.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also run the plugin's unit tests with the following:
|
||||||
|
|
||||||
|
*(Make sure you have `run-in-roblox` installed first!)*
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/unit-test-plugin.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Documentation impacts way more people than the individual lines of code we write.
|
Documentation impacts way more people than the individual lines of code we write.
|
||||||
|
|
||||||
If you find any problems in documentation, including typos, bad grammar, misleading phrasing, or missing content, feel free to file issues and pull requests to fix them.
|
If you find any problems in the documentation, including typos, bad grammar, misleading phrasing, or missing content, feel free to file issues and pull requests to fix them.
|
||||||
|
|
||||||
## Bug Reports and Feature Requests
|
## Bug Reports and Feature Requests
|
||||||
Most of the tools around Rojo try to be clear when an issue is a bug. Even if they aren't, sometimes things don't work quite right.
|
Most of the tools around Rojo try to be clear when an issue is a bug. Even if they aren't, sometimes things don't work quite right.
|
||||||
@@ -30,23 +47,26 @@ Sometimes there's something that Rojo doesn't do that it probably should.
|
|||||||
|
|
||||||
Please file issues and we'll try to help figure out what the best way forward is.
|
Please file issues and we'll try to help figure out what the best way forward is.
|
||||||
|
|
||||||
|
## Local Development Gotchas
|
||||||
|
|
||||||
|
If your build fails with "Error: failed to open file `D:\code\rojo\plugin\modules\roact\src`" you need to update your Git submodules.
|
||||||
|
Run the command and try building again: `git submodule update --init --recursive`.
|
||||||
|
|
||||||
## Pushing a Rojo Release
|
## Pushing a Rojo Release
|
||||||
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
||||||
|
|
||||||
1. Bump server version in [`server/Cargo.toml`](server/Cargo.toml)
|
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
||||||
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
||||||
3. Run `cargo test` to update `Cargo.lock` and double-check tests
|
3. Run `cargo test` to update `Cargo.lock` and run tests
|
||||||
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
5. Commit!
|
5. Commit!
|
||||||
* `git add . && git commit -m "Release vX.Y.Z"`
|
* `git add . && git commit -m "Release vX.Y.Z"`
|
||||||
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
6. Tag the commit
|
||||||
7. Build Windows release build of CLI
|
* `git tag vX.Y.Z`
|
||||||
* `cargo build --release`
|
|
||||||
7. Publish the CLI
|
7. Publish the CLI
|
||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Build and upload the plugin
|
8. Publish the Plugin
|
||||||
* `rojo build plugin -o Rojo.rbxm`
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
* Upload `Rojo.rbxm` to Roblox.com, keep it for later
|
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
|
|||||||
4037
Cargo.lock
generated
166
Cargo.toml
@@ -1,99 +1,133 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "0.6.0-alpha.2"
|
version = "7.7.0-rc.1"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
rust-version = "1.88"
|
||||||
|
authors = [
|
||||||
|
"Lucien Greathouse <me@lpghatguy.com>",
|
||||||
|
"Micah Reid <git@dekkonot.com>",
|
||||||
|
"Ken Loeffler <kenloef@gmail.com>",
|
||||||
|
]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
homepage = "https://rojo.space"
|
homepage = "https://rojo.space"
|
||||||
documentation = "https://rojo.space/docs"
|
documentation = "https://rojo.space/docs"
|
||||||
repository = "https://github.com/rojo-rbx/rojo"
|
repository = "https://github.com/rojo-rbx/rojo"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
exclude = [
|
exclude = ["/test-projects/**"]
|
||||||
"/plugin/**",
|
|
||||||
"/test-projects/**",
|
[profile.dev]
|
||||||
]
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
# Turn on support for specifying glob ignore path rules in the project format.
|
|
||||||
unstable_glob_ignore_paths = []
|
|
||||||
|
|
||||||
# Turn on the server half of Rojo's unstable two-way sync feature.
|
|
||||||
unstable_two_way_sync = []
|
|
||||||
|
|
||||||
# Enable this feature to live-reload assets from the web UI.
|
# Enable this feature to live-reload assets from the web UI.
|
||||||
dev_live_assets = []
|
dev_live_assets = []
|
||||||
|
|
||||||
[workspace]
|
# Run Rojo with this feature to open a Tracy session.
|
||||||
members = [
|
# Currently uses protocol v63, last supported in Tracy 0.9.1.
|
||||||
"rojo-test",
|
profile-with-tracy = ["profiling/profile-with-tracy"]
|
||||||
"rojo-insta-ext",
|
|
||||||
"clibrojo",
|
|
||||||
"vfs",
|
|
||||||
]
|
|
||||||
|
|
||||||
default-members = [
|
[workspace]
|
||||||
".",
|
members = ["crates/*"]
|
||||||
"rojo-test",
|
|
||||||
"rojo-insta-ext",
|
|
||||||
"vfs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "librojo"
|
name = "librojo"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rojo"
|
|
||||||
path = "src/bin.rs"
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "build"
|
name = "build"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.4.0"
|
memofs = { version = "0.3.1", path = "crates/memofs" }
|
||||||
csv = "1.1.1"
|
|
||||||
env_logger = "0.7.1"
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
futures = "0.1.29"
|
# rbx_binary = { path = "../rbx-dom/rbx_binary", features = [
|
||||||
globset = "0.4.4"
|
# "unstable_text_format",
|
||||||
humantime = "1.3.0"
|
# ] }
|
||||||
hyper = "0.12.35"
|
# rbx_dom_weak = { path = "../rbx-dom/rbx_dom_weak" }
|
||||||
jod-thread = "0.1.0"
|
# rbx_reflection = { path = "../rbx-dom/rbx_reflection" }
|
||||||
lazy_static = "1.4.0"
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
log = "0.4.8"
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
maplit = "1.0.1"
|
|
||||||
notify = "4.0.14"
|
rbx_binary = { version = "2.0.1", features = ["unstable_text_format"] }
|
||||||
rbx_binary = "0.5.0"
|
rbx_dom_weak = "4.1.0"
|
||||||
rbx_dom_weak = "1.10.1"
|
rbx_reflection = "6.1.0"
|
||||||
rbx_reflection = "3.3.408"
|
rbx_reflection_database = "2.0.2"
|
||||||
rbx_xml = "0.11.3"
|
rbx_xml = "2.0.1"
|
||||||
regex = "1.3.1"
|
|
||||||
reqwest = "0.9.20"
|
anyhow = "1.0.80"
|
||||||
|
backtrace = "0.3.69"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
crossbeam-channel = "0.5.12"
|
||||||
|
csv = "1.3.0"
|
||||||
|
env_logger = "0.9.3"
|
||||||
|
fs-err = "2.11.0"
|
||||||
|
futures = "0.3.30"
|
||||||
|
globset = "0.4.14"
|
||||||
|
humantime = "2.1.0"
|
||||||
|
hyper = { version = "0.14.28", features = ["server", "tcp", "http1"] }
|
||||||
|
hyper-tungstenite = "0.11.0"
|
||||||
|
jod-thread = "0.1.2"
|
||||||
|
log = "0.4.21"
|
||||||
|
num_cpus = "1.16.0"
|
||||||
|
opener = "0.5.2"
|
||||||
|
rayon = "1.9.0"
|
||||||
|
reqwest = { version = "0.11.24", default-features = false, features = [
|
||||||
|
"blocking",
|
||||||
|
"json",
|
||||||
|
"rustls-tls",
|
||||||
|
] }
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
rlua = "0.17.0"
|
roblox_install = "1.0.0"
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.145"
|
||||||
snafu = "0.6.0"
|
jsonc-parser = { version = "0.27.0", features = ["serde"] }
|
||||||
structopt = "0.3.5"
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
termcolor = "1.0.5"
|
toml = "0.5.11"
|
||||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
termcolor = "1.4.1"
|
||||||
|
thiserror = "1.0.57"
|
||||||
|
tokio = { version = "1.36.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
uuid = { version = "1.7.0", features = ["v4", "serde"] }
|
||||||
|
clap = { version = "3.2.25", features = ["derive"] }
|
||||||
|
profiling = "1.0.15"
|
||||||
|
yaml-rust2 = "0.10.3"
|
||||||
|
data-encoding = "2.8.0"
|
||||||
|
pathdiff = "0.2.3"
|
||||||
|
|
||||||
|
blake3 = "1.5.0"
|
||||||
|
float-cmp = "0.9.0"
|
||||||
|
indexmap = { version = "2.10.0", features = ["serde"] }
|
||||||
|
rmp-serde = "1.3.0"
|
||||||
|
serde_bytes = "0.11.19"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.6.2"
|
winreg = "0.10.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
memofs = { version = "0.3.0", path = "crates/memofs" }
|
||||||
|
|
||||||
|
embed-resource = "1.8.0"
|
||||||
|
anyhow = "1.0.80"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
fs-err = "2.11.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
semver = "1.0.22"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3"
|
criterion = "0.3.6"
|
||||||
insta = { version = "0.13.1", features = ["redactions"] }
|
insta = { version = "1.36.1", features = ["redactions", "yaml", "json"] }
|
||||||
lazy_static = "1.2"
|
paste = "1.0.14"
|
||||||
paste = "0.1"
|
pretty_assertions = "1.4.0"
|
||||||
pretty_assertions = "0.6.1"
|
serde_yaml = "0.8.26"
|
||||||
serde_yaml = "0.8.9"
|
tempfile = "3.10.1"
|
||||||
tempfile = "3.0"
|
walkdir = "2.5.0"
|
||||||
tokio = "0.1.22"
|
|
||||||
walkdir = "2.1"
|
|
||||||
|
|||||||
25
README.md
@@ -1,21 +1,13 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://rojo.space">
|
<a href="https://rojo.space"><img src="assets/brand_images/logo-512.png" alt="Rojo" height="217" /></a>
|
||||||
<img src="assets/logo-512.png" alt="Rojo" height="217" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div> </div>
|
<div> </div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://github.com/rojo-rbx/rojo/actions">
|
<a href="https://github.com/rojo-rbx/rojo/actions"><img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" /></a>
|
||||||
<img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" />
|
<a href="https://crates.io/crates/rojo"><img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" /></a>
|
||||||
</a>
|
<a href="https://rojo.space/docs"><img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" /></a>
|
||||||
<a href="https://crates.io/crates/rojo">
|
|
||||||
<img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" />
|
|
||||||
</a>
|
|
||||||
<a href="https://rojo.space/docs/0.5.x">
|
|
||||||
<img src="https://img.shields.io/badge/docs-0.5.x-brightgreen.svg" alt="Rojo 0.5.x Documentation" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -34,11 +26,10 @@ Rojo enables:
|
|||||||
* Streaming `rbxmx` and `rbxm` models into your game in real time
|
* Streaming `rbxmx` and `rbxm` models into your game in real time
|
||||||
* Packaging and deploying your project to Roblox.com from the command line
|
* Packaging and deploying your project to Roblox.com from the command line
|
||||||
|
|
||||||
Soon, Rojo will be able to:
|
In the future, Rojo will be able to:
|
||||||
|
|
||||||
* Automatically convert your existing game to work with Rojo
|
|
||||||
* Sync instances from Roblox Studio to the filesystem
|
* Sync instances from Roblox Studio to the filesystem
|
||||||
* Automatically manage your assets on Roblox.com, like images and sounds
|
* Automatically convert your existing game to work with Rojo
|
||||||
* Import custom instances like MoonScript code
|
* Import custom instances like MoonScript code
|
||||||
|
|
||||||
## [Documentation](https://rojo.space/docs)
|
## [Documentation](https://rojo.space/docs)
|
||||||
@@ -49,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
|
|||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
Rojo supports Rust 1.40.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
Rojo supports Rust 1.88 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
|
|||||||
BIN
assets/NotificationPop.mp3
Normal file
|
Before Width: | Height: | Size: 975 B After Width: | Height: | Size: 975 B |
BIN
assets/brand_images/icon-link-32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/brand_images/icon-warn-32.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
BIN
assets/images/checkbox-off.png
Normal file
|
After Width: | Height: | Size: 269 B |
15
assets/images/checkbox-off.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-54,-1)">
|
||||||
|
<g id="Artboard3" transform="matrix(1.77778,0,0,1.45455,-42,-0.454545)">
|
||||||
|
<rect x="54" y="1" width="9" height="11" style="fill:none;"/>
|
||||||
|
<g transform="matrix(3.375,0,0,4.125,-3654,-2753.12)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
<g transform="matrix(-1,0,0,1,2200,0)">
|
||||||
|
<path d="M1099,670L1101,668" style="fill:none;stroke:white;stroke-width:0.5px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/checkbox-on.png
Normal file
|
After Width: | Height: | Size: 249 B |
12
assets/images/checkbox-on.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,-32,0)">
|
||||||
|
<g id="Artboard2" transform="matrix(0.8,0,0,0.941176,6.4,0)">
|
||||||
|
<rect x="32" y="0" width="20" height="17" style="fill:none;"/>
|
||||||
|
<g transform="matrix(5,0,0,4.25,-5470.5,-2371.5)">
|
||||||
|
<path d="M1101,560L1102,561L1104,559" style="fill:none;stroke:white;stroke-width:0.75px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 870 B |
BIN
assets/images/circle-128.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/images/circle-16.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
assets/images/circle-32.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
assets/images/circle-500.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/images/circle-64.png
Normal file
|
After Width: | Height: | Size: 613 B |
BIN
assets/images/header-logo.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
21
assets/images/header-logo.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 60 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.952381,0,0,0.710526,-228.571,-156.316)">
|
||||||
|
<rect x="240" y="220" width="63" height="38" style="fill:none;"/>
|
||||||
|
<g transform="matrix(0.0789166,0,0,0.105779,211.848,170.749)">
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,376,753)">
|
||||||
|
<path d="M0.302,-0.836L0.306,-0.836L0.302,-0.829L0.302,-0.814C0.333,-0.82 0.349,-0.828 0.349,-0.836L0.371,-0.833C0.408,-0.835 0.441,-0.836 0.472,-0.836L0.476,-0.836C0.524,-0.836 0.58,-0.81 0.646,-0.757C0.66,-0.734 0.668,-0.714 0.672,-0.695L0.672,-0.659C0.664,-0.609 0.65,-0.571 0.632,-0.546C0.621,-0.537 0.597,-0.507 0.56,-0.456L0.556,-0.456L0.556,-0.463L0.566,-0.478L0.56,-0.478C0.474,-0.405 0.378,-0.349 0.27,-0.311C0.243,-0.304 0.216,-0.3 0.19,-0.3C0.224,-0.26 0.287,-0.207 0.378,-0.141C0.387,-0.136 0.445,-0.099 0.552,-0.028C0.641,0.027 0.747,0.087 0.871,0.153L0.871,0.157L0.856,0.157C0.832,0.148 0.82,0.141 0.82,0.138L0.813,0.142L0.806,0.142C0.803,0.142 0.802,0.141 0.802,0.138C0.793,0.14 0.786,0.149 0.78,0.164C0.795,0.168 0.802,0.173 0.802,0.178C0.796,0.183 0.792,0.186 0.788,0.186L0.77,0.182L0.77,0.186C0.77,0.19 0.773,0.193 0.78,0.193L0.78,0.204L0.777,0.204C0.772,0.204 0.717,0.172 0.614,0.109C0.541,0.072 0.451,0.015 0.346,-0.061C0.311,-0.077 0.247,-0.124 0.153,-0.202L0.143,-0.202C0.129,-0.181 0.111,-0.129 0.088,-0.046C0.083,-0.027 0.059,0.001 0.016,0.037L0.008,0.037L0.001,0.026L0.001,0.022C0.001,0.017 0.005,0.006 0.012,-0.01L0.012,-0.014L0.008,-0.014C0.008,-0.007 -0.007,0.004 -0.035,0.019L-0.039,0.019L-0.039,0.012C0.046,-0.24 0.105,-0.404 0.139,-0.481C0.197,-0.614 0.226,-0.69 0.226,-0.709C0.213,-0.709 0.184,-0.693 0.139,-0.659L0.132,-0.659L0.135,-0.666L0.135,-0.673C0.122,-0.673 0.092,-0.652 0.045,-0.608L0.041,-0.608L0.041,-0.612L0.088,-0.666L0.096,-0.677L0.096,-0.681L0.088,-0.681L0.045,-0.645C0.04,-0.645 0.038,-0.647 0.038,-0.651C0.083,-0.701 0.138,-0.744 0.204,-0.778C0.265,-0.794 0.295,-0.812 0.295,-0.833L0.302,-0.836ZM0.632,-0.735L0.632,-0.731C0.632,-0.727 0.635,-0.724 0.639,-0.724L0.639,-0.728C0.639,-0.732 0.637,-0.735 0.632,-0.735ZM0.208,-0.387L0.211,-0.387C0.255,-0.396 0.277,-0.403 0.277,-0.409C0.274,-0.414 0.273,-0.417 0.273,-0.42C0.365,-0.451 0.427,-0.482 0.458,-0.514C0.461,-0.514 0.48,-0.534 0.516,-0.576L0.52,-0.576L0.52,-0.572C0.515,-0.564 0.512,-0.558 0.512,-0.554L0.516,-0.554C0.547,-0.578 0.563,-0.604 0.563,-0.633L0.563,-0.655C0.556,-0.655 0.552,-0.658 0.552,-0.663C0.564,-0.665 0.57,-0.671 0.57,-0.681C0.57,-0.693 0.546,-0.705 0.498,-0.717C0.459,-0.727 0.417,-0.731 0.371,-0.731C0.348,-0.731 0.325,-0.689 0.302,-0.604C0.292,-0.594 0.261,-0.522 0.208,-0.387ZM0.251,-0.695L0.251,-0.691C0.255,-0.691 0.258,-0.695 0.262,-0.702L0.262,-0.706C0.259,-0.706 0.255,-0.702 0.251,-0.695ZM0.255,-0.626L0.259,-0.626C0.266,-0.629 0.27,-0.636 0.27,-0.648C0.266,-0.648 0.261,-0.641 0.255,-0.626ZM0.596,-0.612L0.596,-0.604C0.599,-0.604 0.603,-0.608 0.606,-0.615L0.606,-0.619L0.603,-0.619C0.598,-0.618 0.596,-0.616 0.596,-0.612ZM0.204,-0.369L0.204,-0.365L0.211,-0.365C0.216,-0.368 0.22,-0.369 0.222,-0.369L0.222,-0.365C0.281,-0.376 0.316,-0.388 0.328,-0.401L0.324,-0.401C0.271,-0.389 0.231,-0.378 0.204,-0.369ZM0.19,-0.333L0.193,-0.333L0.259,-0.347L0.255,-0.354C0.212,-0.349 0.19,-0.341 0.19,-0.333ZM0.334,-0.108L0.334,-0.101C0.372,-0.072 0.395,-0.057 0.403,-0.057C0.394,-0.064 0.389,-0.07 0.389,-0.075L0.334,-0.108ZM0.44,-0.028C0.442,-0.019 0.449,-0.014 0.461,-0.014L0.465,-0.014L0.465,-0.018C0.459,-0.018 0.452,-0.021 0.443,-0.028L0.44,-0.028ZM0.675,0.102C0.694,0.119 0.723,0.136 0.762,0.153L0.766,0.146C0.736,0.128 0.708,0.113 0.683,0.102L0.675,0.102ZM0.875,0.16L0.886,0.16C0.89,0.161 0.893,0.163 0.893,0.167C0.888,0.167 0.886,0.171 0.886,0.178L0.878,0.178L0.882,0.171L0.882,0.167L0.875,0.167L0.875,0.16Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,588.398,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,758.882,753)">
|
||||||
|
<path d="M0.208,-0.7L0.223,-0.7C0.227,-0.7 0.229,-0.697 0.229,-0.691C0.294,-0.687 0.328,-0.684 0.331,-0.682C0.333,-0.682 0.333,-0.683 0.333,-0.685C0.364,-0.683 0.387,-0.682 0.401,-0.682L0.462,-0.682C0.466,-0.682 0.468,-0.684 0.468,-0.688L0.45,-0.688L0.45,-0.694L0.49,-0.694L0.49,-0.688L0.475,-0.688L0.475,-0.682L0.557,-0.682C0.564,-0.682 0.573,-0.681 0.584,-0.679C0.603,-0.681 0.612,-0.685 0.612,-0.691L0.658,-0.691C0.678,-0.691 0.688,-0.692 0.689,-0.694C0.706,-0.692 0.72,-0.691 0.731,-0.691L0.903,-0.691L0.921,-0.688L0.928,-0.691C1.035,-0.686 1.096,-0.681 1.111,-0.676L1.13,-0.682L1.13,-0.676L1.068,-0.667L1.068,-0.664C1.075,-0.664 1.078,-0.661 1.078,-0.657C1.078,-0.654 1.075,-0.651 1.068,-0.648C1.064,-0.65 1.061,-0.651 1.059,-0.651C1.059,-0.647 1.055,-0.645 1.047,-0.645L1.047,-0.642C1.048,-0.638 1.05,-0.636 1.053,-0.636L1.059,-0.636C1.072,-0.636 1.087,-0.635 1.105,-0.633C1.113,-0.635 1.12,-0.636 1.126,-0.636C1.139,-0.625 1.145,-0.619 1.145,-0.618C1.138,-0.613 1.132,-0.611 1.126,-0.611L1.099,-0.611C1.088,-0.611 1.079,-0.608 1.071,-0.602L1.029,-0.602C1.024,-0.602 1.02,-0.603 1.017,-0.605L1.01,-0.602C0.993,-0.604 0.98,-0.605 0.971,-0.605C0.962,-0.605 0.958,-0.604 0.958,-0.602L0.928,-0.605L0.695,-0.605C0.69,-0.605 0.686,-0.604 0.683,-0.602C0.679,-0.604 0.675,-0.605 0.67,-0.605L0.563,-0.605C0.555,-0.605 0.547,-0.604 0.539,-0.602C0.531,-0.604 0.523,-0.605 0.514,-0.605L0.453,-0.605C0.429,-0.578 0.401,-0.525 0.37,-0.446C0.37,-0.439 0.334,-0.358 0.263,-0.201L0.177,-0.021C0.185,-0.001 0.189,0.011 0.189,0.013C0.186,0.013 0.177,0.001 0.162,-0.023L0.156,-0.023L0.153,-0.008C0.159,-0.001 0.162,0.006 0.162,0.013L0.162,0.017L0.15,0.007L0.128,0.007C0.128,-0.005 0.108,-0.045 0.067,-0.112C0.012,-0.235 -0.028,-0.332 -0.052,-0.403L-0.052,-0.412C-0.048,-0.412 -0.046,-0.409 -0.046,-0.403L-0.043,-0.403C-0.039,-0.403 -0.037,-0.405 -0.037,-0.409L-0.046,-0.443L-0.046,-0.446L-0.04,-0.446L0.034,-0.293L0.037,-0.293L0.037,-0.296C0.008,-0.37 -0.006,-0.41 -0.006,-0.416L-0.006,-0.421C0.009,-0.398 0.037,-0.343 0.076,-0.256C0.118,-0.177 0.142,-0.135 0.147,-0.13C0.212,-0.285 0.285,-0.442 0.364,-0.602C0.36,-0.602 0.358,-0.604 0.358,-0.608L0.352,-0.605L0.333,-0.605C0.332,-0.605 0.331,-0.606 0.331,-0.608C0.327,-0.606 0.324,-0.605 0.321,-0.605L0.315,-0.608C0.313,-0.608 0.312,-0.607 0.312,-0.605L0.291,-0.608L0.263,-0.608C0.263,-0.615 0.261,-0.618 0.257,-0.618L0.254,-0.618C0.247,-0.618 0.215,-0.621 0.159,-0.626L0.159,-0.63L0.162,-0.636L0.156,-0.636L0.156,-0.639C0.156,-0.641 0.163,-0.646 0.177,-0.654L0.184,-0.654L0.189,-0.651C0.189,-0.653 0.191,-0.659 0.193,-0.669C0.188,-0.671 0.178,-0.674 0.162,-0.676L0.162,-0.682L0.184,-0.682C0.191,-0.682 0.199,-0.688 0.208,-0.7ZM0.291,-0.697L0.352,-0.697C0.356,-0.697 0.358,-0.694 0.358,-0.691L0.291,-0.691L0.291,-0.697ZM0.41,-0.694L0.419,-0.694L0.419,-0.688L0.41,-0.688L0.41,-0.694ZM0.505,-0.694L0.533,-0.694L0.533,-0.688L0.505,-0.688L0.505,-0.694ZM0.603,-0.694L0.603,-0.691C0.603,-0.687 0.601,-0.685 0.597,-0.685L0.563,-0.685L0.563,-0.691L0.588,-0.691L0.603,-0.694ZM0.202,-0.667L0.202,-0.664L0.239,-0.664L0.239,-0.667L0.202,-0.667ZM0.269,-0.339L0.266,-0.327L0.266,-0.324L0.269,-0.324L0.272,-0.336L0.272,-0.339L0.269,-0.339ZM0.257,-0.308C0.237,-0.266 0.211,-0.205 0.177,-0.125C0.195,-0.147 0.223,-0.207 0.26,-0.305L0.26,-0.308L0.257,-0.308Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(340.635,0,0,340.635,891.444,753)">
|
||||||
|
<path d="M0.631,-0.705C0.646,-0.705 0.661,-0.696 0.673,-0.678L0.646,-0.678C0.62,-0.678 0.587,-0.67 0.546,-0.653L0.546,-0.65L0.549,-0.65C0.607,-0.654 0.649,-0.656 0.677,-0.656L0.683,-0.656L0.683,-0.65L0.67,-0.65C0.668,-0.65 0.667,-0.651 0.667,-0.653L0.503,-0.629C0.503,-0.627 0.493,-0.624 0.473,-0.62L0.473,-0.614L0.519,-0.623L0.521,-0.623L0.521,-0.617C0.386,-0.581 0.318,-0.555 0.318,-0.538C0.252,-0.483 0.204,-0.431 0.175,-0.383C0.131,-0.304 0.108,-0.243 0.108,-0.201C0.108,-0.157 0.131,-0.125 0.175,-0.104C0.183,-0.102 0.19,-0.101 0.196,-0.101C0.286,-0.136 0.353,-0.196 0.397,-0.28C0.409,-0.318 0.419,-0.363 0.427,-0.417L0.434,-0.417L0.427,-0.359C0.433,-0.359 0.439,-0.374 0.443,-0.404C0.441,-0.412 0.439,-0.42 0.439,-0.429L0.439,-0.438L0.446,-0.438C0.448,-0.407 0.449,-0.386 0.449,-0.374C0.449,-0.358 0.444,-0.331 0.434,-0.292C0.445,-0.302 0.458,-0.335 0.473,-0.392C0.48,-0.447 0.486,-0.474 0.491,-0.474C0.491,-0.472 0.492,-0.471 0.494,-0.471C0.492,-0.469 0.49,-0.457 0.488,-0.435L0.488,-0.432L0.491,-0.432C0.499,-0.454 0.504,-0.48 0.506,-0.508C0.502,-0.516 0.5,-0.522 0.5,-0.526L0.506,-0.529C0.512,-0.529 0.518,-0.513 0.525,-0.48C0.525,-0.4 0.491,-0.297 0.424,-0.17C0.372,-0.093 0.305,-0.042 0.224,-0.019C0.188,-0.006 0.154,-0 0.121,-0C0.04,-0.022 -0.001,-0.083 -0.001,-0.183C-0.001,-0.296 0.045,-0.398 0.136,-0.489C0.165,-0.522 0.227,-0.568 0.321,-0.629C0.425,-0.68 0.529,-0.705 0.631,-0.705ZM0.482,-0.656L0.482,-0.653C0.499,-0.653 0.508,-0.652 0.509,-0.65C0.526,-0.659 0.536,-0.663 0.54,-0.663L0.54,-0.668L0.482,-0.656ZM0.099,-0.417C0.144,-0.458 0.179,-0.494 0.206,-0.523C0.23,-0.538 0.242,-0.55 0.242,-0.559C0.201,-0.535 0.156,-0.493 0.108,-0.432C0.102,-0.427 0.099,-0.422 0.099,-0.417ZM0.105,-0.261C0.108,-0.261 0.118,-0.284 0.136,-0.328C0.158,-0.378 0.203,-0.439 0.272,-0.511L0.272,-0.514C0.204,-0.463 0.167,-0.422 0.16,-0.392C0.142,-0.371 0.124,-0.328 0.105,-0.261ZM0.069,-0.334L0.069,-0.332L0.072,-0.332C0.116,-0.393 0.139,-0.429 0.139,-0.438C0.11,-0.413 0.086,-0.378 0.069,-0.334ZM0.467,-0.31L0.467,-0.307L0.47,-0.307C0.492,-0.371 0.503,-0.413 0.503,-0.432C0.49,-0.409 0.478,-0.369 0.467,-0.31ZM0.087,-0.404L0.087,-0.401C0.091,-0.401 0.093,-0.403 0.093,-0.407L0.093,-0.41C0.089,-0.41 0.087,-0.408 0.087,-0.404ZM0.063,-0.368C0.032,-0.297 0.017,-0.245 0.017,-0.213C0.021,-0.148 0.028,-0.107 0.039,-0.088L0.042,-0.088L0.042,-0.094C0.035,-0.107 0.032,-0.129 0.032,-0.158C0.032,-0.191 0.035,-0.227 0.042,-0.268L0.039,-0.273C0.045,-0.3 0.054,-0.331 0.066,-0.365L0.066,-0.368L0.063,-0.368ZM0.096,-0.24L0.096,-0.237C0.1,-0.237 0.103,-0.239 0.103,-0.243L0.103,-0.246C0.098,-0.245 0.096,-0.243 0.096,-0.24ZM0.397,-0.222L0.397,-0.219L0.4,-0.219C0.408,-0.232 0.412,-0.241 0.412,-0.243C0.407,-0.242 0.402,-0.235 0.397,-0.222ZM0.066,-0.24C0.06,-0.222 0.055,-0.202 0.051,-0.179C0.055,-0.138 0.061,-0.108 0.069,-0.088L0.072,-0.088L0.072,-0.094C0.066,-0.121 0.063,-0.139 0.063,-0.146L0.066,-0.152C0.064,-0.155 0.063,-0.16 0.063,-0.164C0.063,-0.169 0.064,-0.173 0.066,-0.176C0.064,-0.18 0.063,-0.183 0.063,-0.186C0.067,-0.218 0.069,-0.235 0.069,-0.237L0.069,-0.24L0.066,-0.24ZM0.294,-0.125L0.294,-0.122C0.3,-0.122 0.322,-0.143 0.357,-0.186L0.357,-0.188L0.354,-0.188C0.314,-0.151 0.294,-0.129 0.294,-0.125ZM0.379,-0.149C0.379,-0.146 0.36,-0.126 0.321,-0.088L0.324,-0.088C0.351,-0.112 0.37,-0.132 0.382,-0.146L0.385,-0.146L0.385,-0.149L0.379,-0.149ZM0.09,-0.104L0.09,-0.101C0.094,-0.08 0.104,-0.07 0.121,-0.07L0.13,-0.07L0.13,-0.073L0.09,-0.104ZM0.151,-0.055L0.188,-0.055L0.203,-0.058L0.203,-0.061L0.157,-0.061C0.153,-0.06 0.151,-0.058 0.151,-0.055ZM0.096,-0.058L0.096,-0.055C0.104,-0.045 0.113,-0.04 0.124,-0.04L0.13,-0.04L0.13,-0.042C0.127,-0.042 0.116,-0.048 0.096,-0.058Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
BIN
assets/images/icons/back.png
Normal file
|
After Width: | Height: | Size: 229 B |
5
assets/images/icons/back.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<path d="M20,11L20,13L8,13L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11L20,11Z" style="fill:white;fill-rule:nonzero;"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 584 B |
BIN
assets/images/icons/close.png
Normal file
|
After Width: | Height: | Size: 295 B |
15
assets/images/icons/close.svg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.0666667,0,0,0.097561,-31,-18.6341)">
|
||||||
|
<rect x="465" y="191" width="360" height="246" style="fill:none;"/>
|
||||||
|
<g transform="matrix(134.328,0,0,102.5,-74228.5,-15214.7)">
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M551,152L550,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.11667,0,0,1,-57.3333,0)">
|
||||||
|
<path d="M550,152L551,151" style="fill:none;stroke:white;stroke-width:0.3px;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/icons/debug.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
assets/images/icons/expand.png
Normal file
|
After Width: | Height: | Size: 273 B |
BIN
assets/images/icons/reset.png
Normal file
|
After Width: | Height: | Size: 933 B |
BIN
assets/images/icons/warning.png
Normal file
|
After Width: | Height: | Size: 241 B |
|
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
BIN
assets/images/rounded-background.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
assets/images/rounded-border.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
assets/images/scrollbar-bottom.png
Normal file
|
After Width: | Height: | Size: 105 B |
17
assets/images/scrollbar-bottom.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-10)">
|
||||||
|
<g id="Bottom" transform="matrix(0.243243,0,0,0.179487,-59.1081,-30.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<clipPath id="_clip1">
|
||||||
|
<rect x="243" y="224" width="37" height="39"/>
|
||||||
|
</clipPath>
|
||||||
|
<g clip-path="url(#_clip1)">
|
||||||
|
<g transform="matrix(-4.11111,6.82303e-16,-3.59619e-16,-3.97959,280,259.816)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/scrollbar-middle.png
Normal file
|
After Width: | Height: | Size: 75 B |
12
assets/images/scrollbar-middle.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-8)">
|
||||||
|
<g id="Middle" transform="matrix(1,0,0,0.111111,0,7.11111)">
|
||||||
|
<rect x="0" y="8" width="9" height="9" style="fill:none;"/>
|
||||||
|
<g transform="matrix(1,0,0,9,0,-64)">
|
||||||
|
<rect x="2" y="8" width="5" height="1" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 796 B |
BIN
assets/images/scrollbar-top.png
Normal file
|
After Width: | Height: | Size: 132 B |
10
assets/images/scrollbar-top.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 9 7" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Top" transform="matrix(0.243243,0,0,0.179487,-59.1081,-40.2051)">
|
||||||
|
<rect x="243" y="224" width="37" height="39" style="fill:none;"/>
|
||||||
|
<g transform="matrix(4.11111,0,0,3.97959,243,227.184)">
|
||||||
|
<path d="M7,5.5C7,3.568 5.88,2 4.5,2C3.12,2 2,3.568 2,5.5L2,9L7,9L7,5.5Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 793 B |
BIN
assets/images/spinner-background.png
Normal file
|
After Width: | Height: | Size: 684 B |
8
assets/images/spinner-background.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M24,0C37.246,0 48,10.754 48,24C48,37.246 37.246,48 24,48C10.754,48 0,37.246 0,24C0,10.754 10.754,0 24,0ZM24,8.4C32.61,8.4 39.6,15.39 39.6,24C39.6,32.61 32.61,39.6 24,39.6C15.39,39.6 8.4,32.61 8.4,24C8.4,15.39 15.39,8.4 24,8.4Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 855 B |
BIN
assets/images/spinner-foreground.png
Normal file
|
After Width: | Height: | Size: 340 B |
8
assets/images/spinner-foreground.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<rect x="0" y="0" width="48" height="48" style="fill:none;"/>
|
||||||
|
<path d="M48,24C48,10.745 37.255,0 24,0L24,8.4C32.616,8.4 39.6,15.384 39.6,24L48,24Z" style="fill:white;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 704 B |
BIN
assets/images/syncsuccess.png
Normal file
|
After Width: | Height: | Size: 574 B |
BIN
assets/images/syncwarning.png
Normal file
|
After Width: | Height: | Size: 607 B |
@@ -17,6 +17,10 @@ html {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #e7e7e7
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width:100%;
|
max-width:100%;
|
||||||
max-height:100%;
|
max-height:100%;
|
||||||
|
|||||||
11
assets/project-templates/model/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build this library, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -o "{project_name}.rbxmx"
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
6
assets/project-templates/model/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
assets/project-templates/model/gitignore.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Roblox Studio lock files
|
||||||
|
/*.rbxlx.lock
|
||||||
|
/*.rbxl.lock
|
||||||
|
|
||||||
|
sourcemap.json
|
||||||
5
assets/project-templates/model/src/init.luau
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
return {
|
||||||
|
hello = function()
|
||||||
|
print("Hello world, from {project_name}!")
|
||||||
|
end,
|
||||||
|
}
|
||||||
17
assets/project-templates/place/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build the place from scratch, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -o "{project_name}.rbxlx"
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, open `{project_name}.rbxlx` in Roblox Studio and start the Rojo server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo serve
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
@@ -1,41 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "[placeholder]",
|
"name": "{project_name}",
|
||||||
"tree": {
|
"tree": {
|
||||||
"$className": "DataModel",
|
"$className": "DataModel",
|
||||||
"HttpService": {
|
|
||||||
"$className": "HttpService",
|
|
||||||
"$properties": {
|
|
||||||
"HttpEnabled": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Lighting": {
|
|
||||||
"$className": "Lighting",
|
|
||||||
"$properties": {
|
|
||||||
"Ambient": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"Brightness": 2,
|
|
||||||
"GlobalShadows": true,
|
|
||||||
"Outlines": false,
|
|
||||||
"Technology": "Voxel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"$className": "ReplicatedStorage",
|
"Shared": {
|
||||||
"Source": {
|
"$path": "src/shared"
|
||||||
"$path": "src"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SoundService": {
|
|
||||||
"$className": "SoundService",
|
"ServerScriptService": {
|
||||||
"$properties": {
|
"Server": {
|
||||||
"RespectFilteringEnabled": true
|
"$path": "src/server"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"StarterPlayer": {
|
||||||
|
"StarterPlayerScripts": {
|
||||||
|
"Client": {
|
||||||
|
"$path": "src/client"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"Workspace": {
|
"Workspace": {
|
||||||
"$className": "Workspace",
|
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"FilteringEnabled": true
|
"FilteringEnabled": true
|
||||||
},
|
},
|
||||||
@@ -61,6 +49,24 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Lighting": {
|
||||||
|
"$properties": {
|
||||||
|
"Ambient": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"Brightness": 2,
|
||||||
|
"GlobalShadows": true,
|
||||||
|
"Outlines": false,
|
||||||
|
"Technology": "Voxel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SoundService": {
|
||||||
|
"$properties": {
|
||||||
|
"RespectFilteringEnabled": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
assets/project-templates/place/gitignore.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Project place file
|
||||||
|
/{project_name}.rbxlx
|
||||||
|
|
||||||
|
# Roblox Studio lock files
|
||||||
|
/*.rbxlx.lock
|
||||||
|
/*.rbxl.lock
|
||||||
|
|
||||||
|
sourcemap.json
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from client!")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from server!")
|
||||||
3
assets/project-templates/place/src/shared/Hello.luau
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
return function()
|
||||||
|
print("Hello, world!")
|
||||||
|
end
|
||||||
17
assets/project-templates/plugin/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# {project_name}
|
||||||
|
Generated by [Rojo](https://github.com/rojo-rbx/rojo) {rojo_version}.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
To build this plugin to your local plugins folder, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -p "{project_name}.rbxm"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can include the `watch` flag to re-build it on save:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rojo build -p "{project_name}.rbxm" --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
For more help, check out [the Rojo documentation](https://rojo.space/docs).
|
||||||
6
assets/project-templates/plugin/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "{project_name}",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
assets/project-templates/plugin/gitignore.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Plugin model files
|
||||||
|
/{project_name}.rbxmx
|
||||||
|
/{project_name}.rbxm
|
||||||
|
|
||||||
|
sourcemap.json
|
||||||
1
assets/project-templates/plugin/src/init.server.luau
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print("Hello world, from plugin!")
|
||||||
@@ -3,7 +3,7 @@ use std::path::Path;
|
|||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
use librojo::cli::{build, BuildCommand};
|
use librojo::cli::BuildCommand;
|
||||||
|
|
||||||
pub fn benchmark_small_place(c: &mut Criterion) {
|
pub fn benchmark_small_place(c: &mut Criterion) {
|
||||||
bench_build_place(c, "Small Place", "test-projects/benchmark_small_place")
|
bench_build_place(c, "Small Place", "test-projects/benchmark_small_place")
|
||||||
@@ -20,7 +20,7 @@ fn bench_build_place(c: &mut Criterion, name: &str, path: &str) {
|
|||||||
group.bench_function("build", |b| {
|
group.bench_function("build", |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| place_setup(path),
|
|| place_setup(path),
|
||||||
|(_dir, options)| build(options).unwrap(),
|
|(_dir, options)| options.run().unwrap(),
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -31,10 +31,12 @@ fn bench_build_place(c: &mut Criterion, name: &str, path: &str) {
|
|||||||
fn place_setup<P: AsRef<Path>>(input_path: P) -> (TempDir, BuildCommand) {
|
fn place_setup<P: AsRef<Path>>(input_path: P) -> (TempDir, BuildCommand) {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
let input = input_path.as_ref().to_path_buf();
|
let input = input_path.as_ref().to_path_buf();
|
||||||
let output = dir.path().join("output.rbxlx");
|
let output = Some(dir.path().join("output.rbxlx"));
|
||||||
|
|
||||||
let options = BuildCommand {
|
let options = BuildCommand {
|
||||||
project: input,
|
project: input,
|
||||||
|
watch: false,
|
||||||
|
plugin: None,
|
||||||
output,
|
output,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
watchexec -c -w plugin "sh -c './bin/install-dev-plugin.sh'"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxm"
|
|
||||||
TESTEZ_FILE="$DIR/TestEZ.rbxm"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/testez.project.json -o "$TESTEZ_FILE"
|
|
||||||
remodel bin/mark-plugin-as-dev.lua "$PLUGIN_FILE" "$TESTEZ_FILE" 2>/dev/null
|
|
||||||
|
|
||||||
cp "$PLUGIN_FILE" "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
rojo build plugin -o "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
local pluginPath, testezPath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local testez = remodel.readModelFile(testezPath)[1]
|
|
||||||
|
|
||||||
local marker = Instance.new("Folder")
|
|
||||||
marker.Name = "ROJO_DEV_BUILD"
|
|
||||||
marker.Parent = plugin
|
|
||||||
|
|
||||||
testez.Parent = plugin
|
|
||||||
|
|
||||||
remodel.writeModelFile(plugin, pluginPath)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
local pluginPath, placePath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local place = remodel.readPlaceFile(placePath)
|
|
||||||
|
|
||||||
plugin.Parent = place:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
remodel.writePlaceFile(place, placePath)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
./bin/run-cli-tests.sh
|
|
||||||
./bin/run-plugin-tests.sh
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo test --all --locked
|
|
||||||
cargo fmt -- --check
|
|
||||||
|
|
||||||
touch src/lib.rs # Nudge Rust source to make Clippy actually check things
|
|
||||||
cargo clippy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxmx"
|
|
||||||
PLACE_FILE="$DIR/RojoTestPlace.rbxlx"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/place.project.json -o "$PLACE_FILE"
|
|
||||||
|
|
||||||
remodel bin/put-plugin-in-test-place.lua "$PLUGIN_FILE" "$PLACE_FILE"
|
|
||||||
|
|
||||||
run-in-roblox -s plugin/testBootstrap.server.lua "$PLACE_FILE"
|
|
||||||
|
|
||||||
luacheck plugin/src plugin/log plugin/http
|
|
||||||
93
build.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use std::{
|
||||||
|
env, io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use fs_err as fs;
|
||||||
|
use fs_err::File;
|
||||||
|
use maplit::hashmap;
|
||||||
|
use memofs::VfsSnapshot;
|
||||||
|
use semver::Version;
|
||||||
|
|
||||||
|
fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
||||||
|
println!("cargo:rerun-if-changed={}", path.display());
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
let mut children = Vec::new();
|
||||||
|
|
||||||
|
for entry in fs::read_dir(path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
|
||||||
|
let file_name = entry.file_name().to_str().unwrap().to_owned();
|
||||||
|
|
||||||
|
if file_name.starts_with(".git") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can skip any TestEZ test files since they aren't necessary for
|
||||||
|
// the plugin to run.
|
||||||
|
if file_name.ends_with(".spec.lua") || file_name.ends_with(".spec.luau") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore images in msgpack-luau because they aren't UTF-8 encoded.
|
||||||
|
if file_name.ends_with(".png") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let child_snapshot = snapshot_from_fs_path(&entry.path())?;
|
||||||
|
children.push((file_name, child_snapshot));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(VfsSnapshot::dir(children))
|
||||||
|
} else {
|
||||||
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
||||||
|
Ok(VfsSnapshot::file(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||||
|
let plugin_dir = root_dir.join("plugin");
|
||||||
|
let templates_dir = root_dir.join("assets").join("project-templates");
|
||||||
|
|
||||||
|
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
|
||||||
|
let plugin_version =
|
||||||
|
Version::parse(fs::read_to_string(plugin_dir.join("Version.txt"))?.trim())?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
our_version, plugin_version,
|
||||||
|
"plugin version does not match Cargo version"
|
||||||
|
);
|
||||||
|
|
||||||
|
let template_snapshot = snapshot_from_fs_path(&templates_dir)?;
|
||||||
|
|
||||||
|
let plugin_snapshot = VfsSnapshot::dir(hashmap! {
|
||||||
|
"default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?,
|
||||||
|
"plugin" => VfsSnapshot::dir(hashmap! {
|
||||||
|
"fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?,
|
||||||
|
"http" => snapshot_from_fs_path(&plugin_dir.join("http"))?,
|
||||||
|
"log" => snapshot_from_fs_path(&plugin_dir.join("log"))?,
|
||||||
|
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_dir.join("rbx_dom_lua"))?,
|
||||||
|
"src" => snapshot_from_fs_path(&plugin_dir.join("src"))?,
|
||||||
|
"Packages" => snapshot_from_fs_path(&plugin_dir.join("Packages"))?,
|
||||||
|
"Version.txt" => snapshot_from_fs_path(&plugin_dir.join("Version.txt"))?,
|
||||||
|
"UploadDetails.json" => snapshot_from_fs_path(&plugin_dir.join("UploadDetails.json"))?,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let template_file = File::create(Path::new(&out_dir).join("templates.bincode"))?;
|
||||||
|
let plugin_file = File::create(Path::new(&out_dir).join("plugin.bincode"))?;
|
||||||
|
|
||||||
|
bincode::serialize_into(plugin_file, &plugin_snapshot)?;
|
||||||
|
bincode::serialize_into(template_file, &template_snapshot)?;
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
|
||||||
|
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
|
||||||
|
embed_resource::compile("build/windows/rojo-manifest.rc");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
2
build/windows/rojo-manifest.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define RT_MANIFEST 24
|
||||||
|
1 RT_MANIFEST "rojo.manifest"
|
||||||
8
build/windows/rojo.manifest
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
|
||||||
|
<ws2:longPathAware>true</ws2:longPathAware>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "clibrojo"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rojo = { path = ".." }
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Rojo as a C Library
|
|
||||||
This is an experiment to expose a C API for Rojo that would be suitable for embedding it into an existing C/C++ application.
|
|
||||||
|
|
||||||
I'm hoping to expand it to drop the HTTP layer and communicate through a channel, which could make it feasible to embed into an existing Roblox IDE with minimal changes or additional code.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
This project is currently not built by default and could break/disappear at any time.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build -p clibrojo
|
|
||||||
```
|
|
||||||
|
|
||||||
On Windows, Cargo will generate a `clibrojo.dll` and associated `.lib` file. Link these into your project.
|
|
||||||
|
|
||||||
To generate the associated C header file to include in the project, use [cbindgen](https://github.com/eqrion/cbindgen):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cbindgen --crate clibrojo --output include/rojo.h
|
|
||||||
```
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use std::{ffi::CStr, os::raw::c_char, path::PathBuf};
|
|
||||||
|
|
||||||
use librojo::commands::{serve, ServeOptions};
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn rojo_serve(path: *const c_char) {
|
|
||||||
let path = unsafe { PathBuf::from(CStr::from_ptr(path).to_str().unwrap()) };
|
|
||||||
|
|
||||||
serve(&ServeOptions {
|
|
||||||
fuzzy_project_path: path,
|
|
||||||
port: None,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
33
crates/memofs/CHANGELOG.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# memofs Changelog
|
||||||
|
|
||||||
|
## Unreleased Changes
|
||||||
|
* Added `Vfs::canonicalize`. [#1201]
|
||||||
|
|
||||||
|
## 0.3.1 (2025-11-27)
|
||||||
|
* Added `Vfs::exists`. [#1169]
|
||||||
|
* Added `create_dir` and `create_dir_all` to allow creating directories. [#937]
|
||||||
|
|
||||||
|
[#1169]: https://github.com/rojo-rbx/rojo/pull/1169
|
||||||
|
[#937]: https://github.com/rojo-rbx/rojo/pull/937
|
||||||
|
|
||||||
|
## 0.3.0 (2024-03-15)
|
||||||
|
* Changed `StdBackend` file watching component to use minimal recursive watches. [#830]
|
||||||
|
* Added `Vfs::read_to_string` and `Vfs::read_to_string_lf_normalized` [#854]
|
||||||
|
|
||||||
|
[#830]: https://github.com/rojo-rbx/rojo/pull/830
|
||||||
|
[#854]: https://github.com/rojo-rbx/rojo/pull/854
|
||||||
|
|
||||||
|
## 0.2.0 (2021-08-23)
|
||||||
|
* Updated to `crossbeam-channel` 0.5.1.
|
||||||
|
|
||||||
|
## 0.1.3 (2020-11-19)
|
||||||
|
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
||||||
|
|
||||||
|
## 0.1.2 (2020-03-29)
|
||||||
|
* `VfsSnapshot` now implements Serde's `Serialize` and `Deserialize` traits.
|
||||||
|
|
||||||
|
## 0.1.1 (2020-03-18)
|
||||||
|
* Improved error messages using the [fs-err](https://crates.io/crates/fs-err) crate.
|
||||||
|
|
||||||
|
## 0.1.0 (2020-03-10)
|
||||||
|
* Initial release
|
||||||
24
crates/memofs/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "memofs"
|
||||||
|
description = "Virtual filesystem with configurable backends."
|
||||||
|
version = "0.3.1"
|
||||||
|
authors = [
|
||||||
|
"Lucien Greathouse <me@lpghatguy.com>",
|
||||||
|
"Micah Reid <git@dekkonot.com>",
|
||||||
|
"Ken Loeffler <kenloef@gmail.com>",
|
||||||
|
]
|
||||||
|
edition = "2018"
|
||||||
|
readme = "README.md"
|
||||||
|
license = "MIT"
|
||||||
|
homepage = "https://github.com/rojo-rbx/rojo/tree/master/memofs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crossbeam-channel = "0.5.12"
|
||||||
|
fs-err = "2.11.0"
|
||||||
|
notify = "4.0.17"
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3.10.1"
|
||||||
22
crates/memofs/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# memofs
|
||||||
|
[](https://crates.io/crates/memofs)
|
||||||
|
|
||||||
|
Implementation of a virtual filesystem with a configurable backend and file
|
||||||
|
watching.
|
||||||
|
|
||||||
|
memofs is currently an unstable minimum viable library. Its primary consumer is
|
||||||
|
[Rojo](https://github.com/rojo-rbx/rojo), a build system for Roblox.
|
||||||
|
|
||||||
|
### Current Features
|
||||||
|
* API similar to `std::fs`
|
||||||
|
* Configurable backends
|
||||||
|
* `StdBackend`, which uses `std::fs` and the `notify` crate
|
||||||
|
* `NoopBackend`, which always throws errors
|
||||||
|
* `InMemoryFs`, a simple in-memory filesystem useful for testing
|
||||||
|
|
||||||
|
### Future Features
|
||||||
|
* Hash-based hierarchical memoization keys (hence the name)
|
||||||
|
* Configurable caching (write-through, write-around, write-back)
|
||||||
|
|
||||||
|
## License
|
||||||
|
memofs is available under the terms of the MIT license. See [LICENSE.txt](LICENSE.txt) or <https://opensource.org/licenses/MIT> for more details.
|
||||||
7
crates/memofs/README.tpl
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# {{crate}}
|
||||||
|
[](https://crates.io/crates/memofs)
|
||||||
|
|
||||||
|
{{readme}}
|
||||||
|
|
||||||
|
## License
|
||||||
|
memofs is available under the terms of the MIT license. See [LICENSE.txt](LICENSE.txt) or <https://opensource.org/licenses/MIT> for more details.
|
||||||
296
crates/memofs/src/in_memory_fs.rs
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
use std::collections::{BTreeSet, HashMap, VecDeque};
|
||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
|
|
||||||
|
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent, VfsSnapshot};
|
||||||
|
|
||||||
|
/// In-memory filesystem that can be used as a VFS backend.
|
||||||
|
///
|
||||||
|
/// Internally reference counted to enable giving a copy to
|
||||||
|
/// [`Vfs`](struct.Vfs.html) and keeping the original to mutate the filesystem's
|
||||||
|
/// state with.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InMemoryFs {
|
||||||
|
inner: Arc<Mutex<InMemoryFsInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryFs {
|
||||||
|
/// Create a new empty `InMemoryFs`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(Mutex::new(InMemoryFsInner::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a [`VfsSnapshot`](enum.VfsSnapshot.html) into a subtree of the
|
||||||
|
/// in-memory filesystem.
|
||||||
|
///
|
||||||
|
/// This function will return an error if the operations required to apply
|
||||||
|
/// the snapshot result in errors, like trying to create a file inside a
|
||||||
|
/// file.
|
||||||
|
pub fn load_snapshot<P: Into<PathBuf>>(
|
||||||
|
&mut self,
|
||||||
|
path: P,
|
||||||
|
snapshot: VfsSnapshot,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.load_snapshot(path.into(), snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raises a filesystem change event.
|
||||||
|
///
|
||||||
|
/// If this `InMemoryFs` is being used as the backend of a
|
||||||
|
/// [`Vfs`](struct.Vfs.html), then any listeners be notified of this event.
|
||||||
|
pub fn raise_event(&mut self, event: VfsEvent) {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
inner.event_sender.send(event).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InMemoryFs {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct InMemoryFsInner {
|
||||||
|
entries: HashMap<PathBuf, Entry>,
|
||||||
|
orphans: BTreeSet<PathBuf>,
|
||||||
|
|
||||||
|
event_receiver: Receiver<VfsEvent>,
|
||||||
|
event_sender: Sender<VfsEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryFsInner {
|
||||||
|
fn new() -> Self {
|
||||||
|
let (event_sender, event_receiver) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
entries: HashMap::new(),
|
||||||
|
orphans: BTreeSet::new(),
|
||||||
|
event_receiver,
|
||||||
|
event_sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_snapshot(&mut self, path: PathBuf, snapshot: VfsSnapshot) -> io::Result<()> {
|
||||||
|
if let Some(parent_path) = path.parent() {
|
||||||
|
if let Some(parent_entry) = self.entries.get_mut(parent_path) {
|
||||||
|
if let Entry::Dir { children } = parent_entry {
|
||||||
|
children.insert(path.clone());
|
||||||
|
} else {
|
||||||
|
return must_be_dir(parent_path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.orphans.insert(path.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.orphans.insert(path.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
match snapshot {
|
||||||
|
VfsSnapshot::File { contents } => {
|
||||||
|
self.entries.insert(path, Entry::File { contents });
|
||||||
|
}
|
||||||
|
VfsSnapshot::Dir { children } => {
|
||||||
|
self.entries.insert(
|
||||||
|
path.clone(),
|
||||||
|
Entry::Dir {
|
||||||
|
children: BTreeSet::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (child_name, child) in children {
|
||||||
|
let full_path = path.join(child_name);
|
||||||
|
self.load_snapshot(full_path, child)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, root_path: PathBuf) {
|
||||||
|
self.orphans.remove(&root_path);
|
||||||
|
|
||||||
|
let mut to_remove = VecDeque::new();
|
||||||
|
to_remove.push_back(root_path);
|
||||||
|
|
||||||
|
while let Some(path) = to_remove.pop_front() {
|
||||||
|
if let Some(Entry::Dir { children }) = self.entries.remove(&path) {
|
||||||
|
to_remove.extend(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Entry {
|
||||||
|
File { contents: Vec<u8> },
|
||||||
|
|
||||||
|
Dir { children: BTreeSet<PathBuf> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsBackend for InMemoryFs {
|
||||||
|
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { contents }) => Ok(contents.clone()),
|
||||||
|
Some(Entry::Dir { .. }) => must_be_file(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
inner.load_snapshot(
|
||||||
|
path.to_path_buf(),
|
||||||
|
VfsSnapshot::File {
|
||||||
|
contents: data.to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&mut self, path: &Path) -> io::Result<bool> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
Ok(inner.entries.contains_key(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::Dir { children }) => {
|
||||||
|
let iter = children
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| Ok(DirEntry { path }));
|
||||||
|
|
||||||
|
Ok(ReadDir {
|
||||||
|
inner: Box::new(iter),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(Entry::File { .. }) => must_be_dir(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_dir(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.load_snapshot(path.to_path_buf(), VfsSnapshot::empty_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_dir_all(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
let mut path_buf = path.to_path_buf();
|
||||||
|
while let Some(parent) = path_buf.parent() {
|
||||||
|
inner.load_snapshot(parent.to_path_buf(), VfsSnapshot::empty_dir())?;
|
||||||
|
path_buf.pop();
|
||||||
|
}
|
||||||
|
inner.load_snapshot(path.to_path_buf(), VfsSnapshot::empty_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { .. }) => {
|
||||||
|
inner.remove(path.to_owned());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(Entry::Dir { .. }) => must_be_file(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::Dir { .. }) => {
|
||||||
|
inner.remove(path.to_owned());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(Entry::File { .. }) => must_be_dir(path),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&mut self, path: &Path) -> io::Result<Metadata> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
match inner.entries.get(path) {
|
||||||
|
Some(Entry::File { .. }) => Ok(Metadata { is_file: true }),
|
||||||
|
Some(Entry::Dir { .. }) => Ok(Metadata { is_file: false }),
|
||||||
|
None => not_found(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We rely on Rojo to prepend cwd to any relative path before storing paths
|
||||||
|
// in MemoFS. The current implementation will error if no prepended absolute path
|
||||||
|
// is found. It really only normalizes paths within the provided path's context.
|
||||||
|
// Example: "/Users/username/project/../other/file.txt" ->
|
||||||
|
// "/Users/username/other/file.txt"
|
||||||
|
// Erroneous example: "/Users/../../other/file.txt" -> "/other/file.txt"
|
||||||
|
// This is not very robust. We should implement proper path normalization here or otherwise
|
||||||
|
// warn if we are missing context and can not fully canonicalize the path correctly.
|
||||||
|
fn canonicalize(&mut self, path: &Path) -> io::Result<PathBuf> {
|
||||||
|
let mut normalized = PathBuf::new();
|
||||||
|
for component in path.components() {
|
||||||
|
match component {
|
||||||
|
std::path::Component::ParentDir => {
|
||||||
|
normalized.pop();
|
||||||
|
}
|
||||||
|
std::path::Component::CurDir => {}
|
||||||
|
_ => normalized.push(component),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
match inner.entries.get(&normalized) {
|
||||||
|
Some(_) => Ok(normalized),
|
||||||
|
None => not_found(&normalized),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
|
inner.event_receiver.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn must_be_file<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::other(format!(
|
||||||
|
"path {} was a directory, but must be a file",
|
||||||
|
path.display()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn must_be_dir<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::other(format!(
|
||||||
|
"path {} was a file, but must be a directory",
|
||||||
|
path.display()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_found<T>(path: &Path) -> io::Result<T> {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
format!("path {} not found", path.display()),
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -1,13 +1,32 @@
|
|||||||
mod memory_backend;
|
/*!
|
||||||
|
Implementation of a virtual filesystem with a configurable backend and file
|
||||||
|
watching.
|
||||||
|
|
||||||
|
memofs is currently an unstable minimum viable library. Its primary consumer is
|
||||||
|
[Rojo](https://github.com/rojo-rbx/rojo), a build system for Roblox.
|
||||||
|
|
||||||
|
## Current Features
|
||||||
|
* API similar to `std::fs`
|
||||||
|
* Configurable backends
|
||||||
|
* `StdBackend`, which uses `std::fs` and the `notify` crate
|
||||||
|
* `NoopBackend`, which always throws errors
|
||||||
|
* `InMemoryFs`, a simple in-memory filesystem useful for testing
|
||||||
|
|
||||||
|
## Future Features
|
||||||
|
* Hash-based hierarchical memoization keys (hence the name)
|
||||||
|
* Configurable caching (write-through, write-around, write-back)
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod in_memory_fs;
|
||||||
mod noop_backend;
|
mod noop_backend;
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
mod std_backend;
|
mod std_backend;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
|
use std::{io, str};
|
||||||
|
|
||||||
pub use memory_backend::MemoryBackend;
|
pub use in_memory_fs::InMemoryFs;
|
||||||
pub use noop_backend::NoopBackend;
|
pub use noop_backend::NoopBackend;
|
||||||
pub use snapshot::VfsSnapshot;
|
pub use snapshot::VfsSnapshot;
|
||||||
pub use std_backend::StdBackend;
|
pub use std_backend::StdBackend;
|
||||||
@@ -18,9 +37,9 @@ mod sealed {
|
|||||||
/// Sealing trait for VfsBackend.
|
/// Sealing trait for VfsBackend.
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
|
|
||||||
impl Sealed for MemoryBackend {}
|
|
||||||
impl Sealed for NoopBackend {}
|
impl Sealed for NoopBackend {}
|
||||||
impl Sealed for StdBackend {}
|
impl Sealed for StdBackend {}
|
||||||
|
impl Sealed for InMemoryFs {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait that transforms `io::Result<T>` into `io::Result<Option<T>>`.
|
/// Trait that transforms `io::Result<T>` into `io::Result<Option<T>>`.
|
||||||
@@ -51,10 +70,14 @@ impl<T> IoResultExt<T> for io::Result<T> {
|
|||||||
pub trait VfsBackend: sealed::Sealed + Send + 'static {
|
pub trait VfsBackend: sealed::Sealed + Send + 'static {
|
||||||
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>>;
|
fn read(&mut self, path: &Path) -> io::Result<Vec<u8>>;
|
||||||
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()>;
|
fn write(&mut self, path: &Path, data: &[u8]) -> io::Result<()>;
|
||||||
|
fn exists(&mut self, path: &Path) -> io::Result<bool>;
|
||||||
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir>;
|
fn read_dir(&mut self, path: &Path) -> io::Result<ReadDir>;
|
||||||
|
fn create_dir(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
fn create_dir_all(&mut self, path: &Path) -> io::Result<()>;
|
||||||
fn metadata(&mut self, path: &Path) -> io::Result<Metadata>;
|
fn metadata(&mut self, path: &Path) -> io::Result<Metadata>;
|
||||||
fn remove_file(&mut self, path: &Path) -> io::Result<()>;
|
fn remove_file(&mut self, path: &Path) -> io::Result<()>;
|
||||||
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()>;
|
fn remove_dir_all(&mut self, path: &Path) -> io::Result<()>;
|
||||||
|
fn canonicalize(&mut self, path: &Path) -> io::Result<PathBuf>;
|
||||||
|
|
||||||
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent>;
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent>;
|
||||||
fn watch(&mut self, path: &Path) -> io::Result<()>;
|
fn watch(&mut self, path: &Path) -> io::Result<()>;
|
||||||
@@ -107,6 +130,8 @@ impl Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents an event that a filesystem can raise that might need to be
|
||||||
|
/// handled.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum VfsEvent {
|
pub enum VfsEvent {
|
||||||
@@ -119,16 +144,44 @@ pub enum VfsEvent {
|
|||||||
/// the public interfaces to this type.
|
/// the public interfaces to this type.
|
||||||
struct VfsInner {
|
struct VfsInner {
|
||||||
backend: Box<dyn VfsBackend>,
|
backend: Box<dyn VfsBackend>,
|
||||||
|
watch_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VfsInner {
|
impl VfsInner {
|
||||||
fn read<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<Vec<u8>>> {
|
fn read<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<Vec<u8>>> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let contents = self.backend.read(path)?;
|
let contents = self.backend.read(path)?;
|
||||||
self.backend.watch(path)?;
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Arc::new(contents))
|
Ok(Arc::new(contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_to_string<P: AsRef<Path>>(&mut self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = self.backend.read(path)?;
|
||||||
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contents_str = str::from_utf8(&contents).map_err(|_| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
format!("File was not valid UTF-8: {}", path.display()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Arc::new(contents_str.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists<P: AsRef<Path>>(&mut self, path: P) -> io::Result<bool> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.backend.exists(path)
|
||||||
|
}
|
||||||
|
|
||||||
fn write<P: AsRef<Path>, C: AsRef<[u8]>>(&mut self, path: P, contents: C) -> io::Result<()> {
|
fn write<P: AsRef<Path>, C: AsRef<[u8]>>(&mut self, path: P, contents: C) -> io::Result<()> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let contents = contents.as_ref();
|
let contents = contents.as_ref();
|
||||||
@@ -138,10 +191,24 @@ impl VfsInner {
|
|||||||
fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> {
|
fn read_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<ReadDir> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let dir = self.backend.read_dir(path)?;
|
let dir = self.backend.read_dir(path)?;
|
||||||
self.backend.watch(path)?;
|
|
||||||
|
if self.watch_enabled {
|
||||||
|
self.backend.watch(path)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(dir)
|
Ok(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.backend.create_dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_dir_all<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.backend.create_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
fn remove_file<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let _ = self.backend.unwatch(path);
|
let _ = self.backend.unwatch(path);
|
||||||
@@ -159,16 +226,18 @@ impl VfsInner {
|
|||||||
self.backend.metadata(path)
|
self.backend.metadata(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn canonicalize<P: AsRef<Path>>(&mut self, path: P) -> io::Result<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.backend.canonicalize(path)
|
||||||
|
}
|
||||||
|
|
||||||
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
self.backend.event_receiver()
|
self.backend.event_receiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> {
|
fn commit_event(&mut self, event: &VfsEvent) -> io::Result<()> {
|
||||||
match event {
|
if let VfsEvent::Remove(path) = event {
|
||||||
VfsEvent::Remove(path) => {
|
let _ = self.backend.unwatch(path);
|
||||||
let _ = self.backend.unwatch(&path);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -176,6 +245,10 @@ impl VfsInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A virtual filesystem with a configurable backend.
|
/// A virtual filesystem with a configurable backend.
|
||||||
|
///
|
||||||
|
/// All operations on the Vfs take a lock on an internal backend. For performing
|
||||||
|
/// large batches of operations, it might be more performant to call `lock()`
|
||||||
|
/// and use [`VfsLock`](struct.VfsLock.html) instead.
|
||||||
pub struct Vfs {
|
pub struct Vfs {
|
||||||
inner: Mutex<VfsInner>,
|
inner: Mutex<VfsInner>,
|
||||||
}
|
}
|
||||||
@@ -190,6 +263,7 @@ impl Vfs {
|
|||||||
pub fn new<B: VfsBackend>(backend: B) -> Self {
|
pub fn new<B: VfsBackend>(backend: B) -> Self {
|
||||||
let lock = VfsInner {
|
let lock = VfsInner {
|
||||||
backend: Box::new(backend),
|
backend: Box::new(backend),
|
||||||
|
watch_enabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -197,12 +271,23 @@ impl Vfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manually lock the Vfs, useful for large batches of operations.
|
||||||
pub fn lock(&self) -> VfsLock<'_> {
|
pub fn lock(&self) -> VfsLock<'_> {
|
||||||
VfsLock {
|
VfsLock {
|
||||||
inner: self.inner.lock().unwrap(),
|
inner: self.inner.lock().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns automatic file watching on or off. Enabled by default.
|
||||||
|
///
|
||||||
|
/// Turning off file watching may be useful for single-use cases, especially
|
||||||
|
/// on platforms like macOS where registering file watches has significant
|
||||||
|
/// performance cost.
|
||||||
|
pub fn set_watch_enabled(&self, enabled: bool) {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.watch_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a file from the VFS, or the underlying backend if it isn't
|
/// Read a file from the VFS, or the underlying backend if it isn't
|
||||||
/// resident.
|
/// resident.
|
||||||
///
|
///
|
||||||
@@ -215,6 +300,33 @@ impl Vfs {
|
|||||||
self.inner.lock().unwrap().read(path)
|
self.inner.lock().unwrap().read(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS (or from the underlying backend if it isn't
|
||||||
|
/// resident) into a string.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string].
|
||||||
|
///
|
||||||
|
/// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().read_to_string(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a file from the VFS (or the underlying backend if it isn't
|
||||||
|
/// resident) into a string, and normalize its line endings to LF.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::read_to_string`][std::fs::read_to_string], but also performs
|
||||||
|
/// line ending normalization.
|
||||||
|
///
|
||||||
|
/// [std::fs::read_to_string]: https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html
|
||||||
|
#[inline]
|
||||||
|
pub fn read_to_string_lf_normalized<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let contents = self.inner.lock().unwrap().read_to_string(path)?;
|
||||||
|
|
||||||
|
Ok(contents.replace("\r\n", "\n").into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Write a file to the VFS and the underlying backend.
|
/// Write a file to the VFS and the underlying backend.
|
||||||
///
|
///
|
||||||
/// Roughly equivalent to [`std::fs::write`][std::fs::write].
|
/// Roughly equivalent to [`std::fs::write`][std::fs::write].
|
||||||
@@ -238,6 +350,42 @@ impl Vfs {
|
|||||||
self.inner.lock().unwrap().read_dir(path)
|
self.inner.lock().unwrap().read_dir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether the given path exists.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::exists`][std::fs::exists].
|
||||||
|
///
|
||||||
|
/// [std::fs::exists]: https://doc.rust-lang.org/stable/std/fs/fn.exists.html
|
||||||
|
#[inline]
|
||||||
|
pub fn exists<P: AsRef<Path>>(&self, path: P) -> io::Result<bool> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().exists(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a directory at the provided location.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::create_dir`][std::fs::create_dir].
|
||||||
|
/// Similiar to that function, this function will fail if the parent of the
|
||||||
|
/// path does not exist.
|
||||||
|
///
|
||||||
|
/// [std::fs::create_dir]: https://doc.rust-lang.org/stable/std/fs/fn.create_dir.html
|
||||||
|
#[inline]
|
||||||
|
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().create_dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a directory at the provided location, recursively creating
|
||||||
|
/// all parent components if they are missing.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::create_dir_all`][std::fs::create_dir_all].
|
||||||
|
///
|
||||||
|
/// [std::fs::create_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.create_dir_all.html
|
||||||
|
#[inline]
|
||||||
|
pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().create_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a file.
|
/// Remove a file.
|
||||||
///
|
///
|
||||||
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
||||||
@@ -271,6 +419,19 @@ impl Vfs {
|
|||||||
self.inner.lock().unwrap().metadata(path)
|
self.inner.lock().unwrap().metadata(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a path via the underlying backend.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::canonicalize`][std::fs::canonicalize]. Relative paths are
|
||||||
|
/// resolved against the backend's current working directory (if applicable) and errors are
|
||||||
|
/// surfaced directly from the backend.
|
||||||
|
///
|
||||||
|
/// [std::fs::canonicalize]: https://doc.rust-lang.org/stable/std/fs/fn.canonicalize.html
|
||||||
|
#[inline]
|
||||||
|
pub fn canonicalize<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.lock().unwrap().canonicalize(path)
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve a handle to the event receiver for this `Vfs`.
|
/// Retrieve a handle to the event receiver for this `Vfs`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
@@ -284,12 +445,23 @@ impl Vfs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A locked handle to a `Vfs`, created by `Vfs::lock`.
|
/// A locked handle to a [`Vfs`](struct.Vfs.html), created by `Vfs::lock`.
|
||||||
|
///
|
||||||
|
/// Implements roughly the same API as [`Vfs`](struct.Vfs.html).
|
||||||
pub struct VfsLock<'a> {
|
pub struct VfsLock<'a> {
|
||||||
inner: MutexGuard<'a, VfsInner>,
|
inner: MutexGuard<'a, VfsInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VfsLock<'_> {
|
impl VfsLock<'_> {
|
||||||
|
/// Turns automatic file watching on or off. Enabled by default.
|
||||||
|
///
|
||||||
|
/// Turning off file watching may be useful for single-use cases, especially
|
||||||
|
/// on platforms like macOS where registering file watches has significant
|
||||||
|
/// performance cost.
|
||||||
|
pub fn set_watch_enabled(&mut self, enabled: bool) {
|
||||||
|
self.inner.watch_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a file from the VFS, or the underlying backend if it isn't
|
/// Read a file from the VFS, or the underlying backend if it isn't
|
||||||
/// resident.
|
/// resident.
|
||||||
///
|
///
|
||||||
@@ -329,6 +501,31 @@ impl VfsLock<'_> {
|
|||||||
self.inner.read_dir(path)
|
self.inner.read_dir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a directory at the provided location.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::create_dir`][std::fs::create_dir].
|
||||||
|
/// Similiar to that function, this function will fail if the parent of the
|
||||||
|
/// path does not exist.
|
||||||
|
///
|
||||||
|
/// [std::fs::create_dir]: https://doc.rust-lang.org/stable/std/fs/fn.create_dir.html
|
||||||
|
#[inline]
|
||||||
|
pub fn create_dir<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.create_dir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a directory at the provided location, recursively creating
|
||||||
|
/// all parent components if they are missing.
|
||||||
|
///
|
||||||
|
/// Roughly equivalent to [`std::fs::create_dir_all`][std::fs::create_dir_all].
|
||||||
|
///
|
||||||
|
/// [std::fs::create_dir_all]: https://doc.rust-lang.org/stable/std/fs/fn.create_dir_all.html
|
||||||
|
#[inline]
|
||||||
|
pub fn create_dir_all<P: AsRef<Path>>(&mut self, path: P) -> io::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.create_dir_all(path)
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a file.
|
/// Remove a file.
|
||||||
///
|
///
|
||||||
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
/// Roughly equivalent to [`std::fs::remove_file`][std::fs::remove_file].
|
||||||
@@ -362,6 +559,13 @@ impl VfsLock<'_> {
|
|||||||
self.inner.metadata(path)
|
self.inner.metadata(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a path via the underlying backend.
|
||||||
|
#[inline]
|
||||||
|
pub fn normalize<P: AsRef<Path>>(&mut self, path: P) -> io::Result<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
self.inner.canonicalize(path)
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve a handle to the event receiver for this `Vfs`.
|
/// Retrieve a handle to the event receiver for this `Vfs`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
pub fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
@@ -374,3 +578,83 @@ impl VfsLock<'_> {
|
|||||||
self.inner.commit_event(event)
|
self.inner.commit_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{InMemoryFs, StdBackend, Vfs, VfsSnapshot};
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// https://github.com/rojo-rbx/rojo/issues/899
|
||||||
|
#[test]
|
||||||
|
fn read_to_string_lf_normalized_keeps_trailing_newline() {
|
||||||
|
let mut imfs = InMemoryFs::new();
|
||||||
|
imfs.load_snapshot("test", VfsSnapshot::file("bar\r\nfoo\r\n\r\n"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let vfs = Vfs::new(imfs);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vfs.read_to_string_lf_normalized("test").unwrap().as_str(),
|
||||||
|
"bar\nfoo\n\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://github.com/rojo-rbx/rojo/issues/1200
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_in_memory_success() {
|
||||||
|
let mut imfs = InMemoryFs::new();
|
||||||
|
let contents = "Lorem ipsum dolor sit amet.".to_string();
|
||||||
|
|
||||||
|
imfs.load_snapshot("/test/file.txt", VfsSnapshot::file(contents.to_string()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let vfs = Vfs::new(imfs);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
vfs.canonicalize("/test/nested/../file.txt").unwrap(),
|
||||||
|
PathBuf::from("/test/file.txt")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vfs.read_to_string(vfs.canonicalize("/test/nested/../file.txt").unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
contents.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_in_memory_missing_errors() {
|
||||||
|
let imfs = InMemoryFs::new();
|
||||||
|
let vfs = Vfs::new(imfs);
|
||||||
|
|
||||||
|
let err = vfs.canonicalize("test").unwrap_err();
|
||||||
|
assert_eq!(err.kind(), io::ErrorKind::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_std_backend_success() {
|
||||||
|
let contents = "Lorem ipsum dolor sit amet.".to_string();
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let file_path = dir.path().join("file.txt");
|
||||||
|
fs_err::write(&file_path, contents.to_string()).unwrap();
|
||||||
|
|
||||||
|
let vfs = Vfs::new(StdBackend::new());
|
||||||
|
let canonicalized = vfs.canonicalize(&file_path).unwrap();
|
||||||
|
assert_eq!(canonicalized, file_path.canonicalize().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
vfs.read_to_string(&canonicalized).unwrap().to_string(),
|
||||||
|
contents.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canonicalize_std_backend_missing_errors() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let file_path = dir.path().join("test");
|
||||||
|
|
||||||
|
let vfs = Vfs::new(StdBackend::new());
|
||||||
|
let err = vfs.canonicalize(&file_path).unwrap_err();
|
||||||
|
assert_eq!(err.kind(), io::ErrorKind::NotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
74
crates/memofs/src/noop_backend.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use crate::{Metadata, ReadDir, VfsBackend, VfsEvent};
|
||||||
|
|
||||||
|
/// `VfsBackend` that returns an error on every operation.
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct NoopBackend;
|
||||||
|
|
||||||
|
impl NoopBackend {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsBackend for NoopBackend {
|
||||||
|
fn read(&mut self, _path: &Path) -> io::Result<Vec<u8>> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&mut self, _path: &Path) -> io::Result<bool> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir(&mut self, _path: &Path) -> io::Result<ReadDir> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_dir(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_dir_all(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&mut self, _path: &Path) -> io::Result<Metadata> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn canonicalize(&mut self, _path: &Path) -> io::Result<PathBuf> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_receiver(&self) -> crossbeam_channel::Receiver<VfsEvent> {
|
||||||
|
crossbeam_channel::never()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwatch(&mut self, _path: &Path) -> io::Result<()> {
|
||||||
|
Err(io::Error::other("NoopBackend doesn't do anything"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NoopBackend {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
/// A slice of a tree of files. Can be loaded into an
|
||||||
|
/// [`InMemoryFs`](struct.InMemoryFs.html).
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum VfsSnapshot {
|
pub enum VfsSnapshot {
|
||||||
File {
|
File {
|
||||||
@@ -26,4 +30,16 @@ impl VfsSnapshot {
|
|||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn empty_file() -> Self {
|
||||||
|
Self::File {
|
||||||
|
contents: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty_dir() -> Self {
|
||||||
|
Self::Dir {
|
||||||
|
children: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||