Compare commits
232 commits
main
...
conditiona
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd10f5d587 | ||
|
|
6dfcdd1007 | ||
|
|
317fba1d6b | ||
|
|
3169d159ca | ||
|
|
b2b37e04e4 | ||
|
|
dac9cf23a2 | ||
|
|
767b78cc30 | ||
|
|
bce0481f40 | ||
|
|
ec0fe42968 | ||
|
|
f75c3988b3 | ||
|
|
16d25c9746 | ||
|
|
9c70df1539 | ||
|
|
4ef0274b44 | ||
|
|
304c2bad6c | ||
|
|
eb2fe9494b | ||
|
|
8b2c6e7684 | ||
|
|
95d46a0e5d | ||
|
|
308c03b11b | ||
|
|
79e57ecb49 | ||
|
|
0a2aea2c9b | ||
|
|
b4f3cddb50 | ||
|
|
7385f48e46 | ||
|
|
a42354c743 | ||
|
|
166a7a417d | ||
|
|
e5eceb3879 | ||
|
|
a5a61580cd | ||
|
|
1648b864c1 | ||
|
|
6fab6d700a | ||
|
|
0b7f27c19a | ||
|
|
803d7b582e | ||
|
|
b411d04c8c | ||
|
|
903eb5629c | ||
|
|
6353450a94 | ||
|
|
2d6856c8eb | ||
|
|
c73b741a24 | ||
|
|
a8ab341f1e | ||
|
|
b7bafaaf85 | ||
|
|
cf97c2274a | ||
|
|
0985d26a54 | ||
|
|
8f788e27b0 | ||
|
|
3c08490c96 | ||
|
|
28e96baf3a | ||
|
|
02da2b82a1 | ||
|
|
782c733b90 | ||
|
|
97e89d76a5 | ||
|
|
662905e5e3 | ||
|
|
e13ae7b725 | ||
|
|
c92596f549 | ||
|
|
e4b32a7a32 | ||
|
|
9d83400a51 | ||
|
|
761717cb4c | ||
|
|
d1317d5a8c | ||
|
|
90047b4532 | ||
|
|
89ec6fbe67 | ||
|
|
4a8f584176 | ||
|
|
bcfb38d5f9 | ||
|
|
4e1a26aad0 | ||
|
|
d26a51fae8 | ||
|
|
56d8a0b598 | ||
|
|
a1bcfa2479 | ||
|
|
0b0862b20c | ||
|
|
e9ca7ab453 | ||
|
|
44fb67b98d | ||
|
|
e8a50fb120 | ||
|
|
27b6530599 | ||
|
|
0170662c3c | ||
|
|
41abd500f5 | ||
|
|
fa9be73c15 | ||
|
|
51ac627491 | ||
|
|
edfb51c5ed | ||
|
|
ad66635c15 | ||
|
|
31a80b1794 | ||
|
|
5f00c18bd9 | ||
|
|
614c7e2a36 | ||
|
|
09369d27ec | ||
|
|
0d205f9376 | ||
|
|
82e0acebe0 | ||
|
|
1879130f45 | ||
|
|
9f73b9a33c | ||
|
|
a14f6bcf6d | ||
|
|
91fb37cbe3 | ||
|
|
20856edaa1 | ||
|
|
aa1be48d39 | ||
|
|
0af88b1494 | ||
|
|
5542340a75 | ||
|
|
457d2b1b1c | ||
|
|
d32228628e | ||
|
|
1e7dd3a0c4 | ||
|
|
61dc99f4ad | ||
|
|
05d0a6f8d2 | ||
|
|
aaca7c4c4c | ||
|
|
64c7d0efe7 | ||
|
|
b33c30284f | ||
|
|
2e031337ce | ||
|
|
0e7f936995 | ||
|
|
3e16028406 | ||
|
|
bd970d03f3 | ||
|
|
d3f22216c1 | ||
|
|
5366d242c2 | ||
|
|
42e75ba4b8 | ||
|
|
91e91e067d | ||
|
|
0bb275797f | ||
|
|
87d7b91ac7 | ||
|
|
9faf10958b | ||
|
|
7a86f3ae23 | ||
|
|
696810fd5a | ||
|
|
42e168b35c | ||
|
|
63252a57e4 | ||
|
|
8f1987567b | ||
|
|
68260bd9e2 | ||
|
|
73272faaa8 | ||
|
|
8b4862b7c7 | ||
|
|
0198e84c90 | ||
|
|
9a52f90fe9 | ||
|
|
9fcfea47a6 | ||
|
|
28e8fdf24c | ||
|
|
a2fa693276 | ||
|
|
d58d5b2797 | ||
|
|
00e73b0881 | ||
|
|
4f404ff15f | ||
|
|
9c828ff898 | ||
|
|
12b670cc6b | ||
|
|
77ae72402e | ||
|
|
4c34b2ed34 | ||
|
|
ff26372570 | ||
|
|
237e71598d | ||
|
|
95a4fcd1a9 | ||
|
|
739df89264 | ||
|
|
922e7d9407 | ||
|
|
4f0bb0b73a | ||
|
|
05e307bb6c | ||
|
|
26c4cb3b5c | ||
|
|
778bea7567 | ||
|
|
cfc853b741 | ||
|
|
eeec89a07a | ||
|
|
0f0d06074c | ||
|
|
a93c61f635 | ||
|
|
703a8b014d | ||
|
|
86fb660593 | ||
|
|
e4b398b24f | ||
|
|
5e24f8e08a | ||
|
|
a72d97cebb | ||
|
|
ae4d21308d | ||
|
|
e0c52a9332 | ||
|
|
757879442f | ||
|
|
2a58e7c431 | ||
|
|
dba5043591 | ||
|
|
6eed8cb3f6 | ||
|
|
6e8c5c9868 | ||
|
|
88316a18e5 | ||
|
|
7f31ac15ff | ||
|
|
adadf807fa | ||
|
|
3664fd5e02 | ||
|
|
ceba1321ec | ||
|
|
516c1498c7 | ||
|
|
bc1af02a56 | ||
|
|
b6b3f854a7 | ||
|
|
3779be2bab | ||
|
|
adbc7593dc | ||
|
|
2bfdbce964 | ||
|
|
01d1b9a607 | ||
|
|
b57f4d74e2 | ||
|
|
37575ea0bd | ||
|
|
40cb82ce75 | ||
|
|
615c157a27 | ||
|
|
6722e78b35 | ||
|
|
e4af07f7ad | ||
|
|
c6370e15f9 | ||
|
|
9f591a5586 | ||
|
|
a2196b06ab | ||
|
|
eae2b7a3f4 | ||
|
|
776d5bf614 | ||
|
|
ac6e8733e9 | ||
|
|
a7a1f9347c | ||
|
|
86f9c6f092 | ||
|
|
3be258c218 | ||
|
|
ef2ebb28d5 | ||
|
|
ea3d9cd57a | ||
|
|
e51b09ca65 | ||
|
|
eb8d9294c5 | ||
|
|
55daf8e0f5 | ||
|
|
e572dc645a | ||
|
|
571c27e1f1 | ||
|
|
fd74f04437 | ||
|
|
f661c0371c | ||
|
|
729dafeb82 | ||
|
|
b05b617601 | ||
|
|
9794b83985 | ||
|
|
5ddd9c4db6 | ||
|
|
5c7a8e98ea | ||
|
|
352c563d99 | ||
|
|
ecebde1d52 | ||
|
|
c9138b2e0b | ||
|
|
cb1a0e1a95 | ||
|
|
e1d4955431 | ||
|
|
69d86873c3 | ||
|
|
7248f2121e | ||
|
|
820815be0b | ||
|
|
8719be3944 | ||
|
|
fd500fcd0c | ||
|
|
0917fcaa33 | ||
|
|
04c4de1997 | ||
|
|
88ab01c7f8 | ||
|
|
49400f90f1 | ||
|
|
581cf6abcc | ||
|
|
0af64ee3e8 | ||
|
|
9ebaaaccb3 | ||
|
|
f49b054b7d | ||
|
|
0e171fd2dd | ||
|
|
764f6a19d4 | ||
|
|
2f21b22049 | ||
|
|
dce8280d76 | ||
|
|
80415ece5e | ||
|
|
ae5d746226 | ||
|
|
8af8e0d1e6 | ||
|
|
3e015818d6 | ||
|
|
9b6047d639 | ||
|
|
9799e88244 | ||
|
|
a4314c052b | ||
|
|
3309d3f635 | ||
|
|
225a3fa3c6 | ||
|
|
71205d8fcb | ||
|
|
f8506be208 | ||
|
|
7dbb59c9db | ||
|
|
251cc37d7d | ||
|
|
960cb40c6b | ||
|
|
cdca9cbcfe | ||
|
|
9a57badc9f | ||
|
|
7b3fe8de0f | ||
|
|
1f74b14513 | ||
|
|
1f1a6faf2c | ||
|
|
ef056a57d3 |
628 changed files with 73823 additions and 56724 deletions
|
|
@ -7,3 +7,11 @@ end_of_line = lf
|
|||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@
|
|||
/src/corePluginList.js
|
||||
/oxide/crates/node/index.js
|
||||
/oxide/crates/node/index.d.ts
|
||||
.github/actions/minus-one/index.mjs
|
||||
7
.github/actions/minus-one/index.mjs
vendored
Normal file
7
.github/actions/minus-one/index.mjs
vendored
Normal file
File diff suppressed because one or more lines are too long
659
.github/actions/minus-one/licenses.txt
vendored
Normal file
659
.github/actions/minus-one/licenses.txt
vendored
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
@actions/core
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@actions/github
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@actions/http-client
|
||||
MIT
|
||||
Actions Http Client for Node.js
|
||||
|
||||
Copyright (c) GitHub, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/auth-token
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/core
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/endpoint
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/graphql
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/plugin-paginate-rest
|
||||
MIT
|
||||
MIT License Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/plugin-rest-endpoint-methods
|
||||
MIT
|
||||
MIT License Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/request
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2018 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@octokit/request-error
|
||||
MIT
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
@vercel/ncc
|
||||
MIT
|
||||
Copyright 2018 ZEIT, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
before-after-hook
|
||||
Apache-2.0
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 Gregor Martynus and other contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
deprecation
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Gregor Martynus and contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
is-plain-object
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2017, Jon Schlinkert.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
nextjs-project
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Vercel, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
node-fetch
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 David Frank
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
once
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
tr46
|
||||
MIT
|
||||
|
||||
tunnel
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012 Koichi Kobayashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
universal-user-agent
|
||||
ISC
|
||||
# [ISC License](https://spdx.org/licenses/ISC)
|
||||
|
||||
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
uuid
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2010-2020 Robert Kieffer and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
webidl-conversions
|
||||
BSD-2-Clause
|
||||
# The BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2014, Domenic Denicola
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
whatwg-url
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015–2016 Sebastian Mayr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
wrappy
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
572
.github/actions/minus-one/package-lock.json
generated
vendored
Normal file
572
.github/actions/minus-one/package-lock.json
generated
vendored
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
{
|
||||
"name": "minus-one",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@actions/core": "1.10.0",
|
||||
"@actions/github": "5.1.1",
|
||||
"@octokit/graphql": "7.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "0.34.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
|
||||
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz",
|
||||
"integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.0.1",
|
||||
"@octokit/types": "^11.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz",
|
||||
"integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^11.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
|
||||
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/request": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz",
|
||||
"integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^9.0.0",
|
||||
"@octokit/request-error": "^5.0.0",
|
||||
"@octokit/types": "^11.1.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/request-error": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz",
|
||||
"integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^11.0.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz",
|
||||
"integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "12.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
|
||||
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "2.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
|
||||
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
|
||||
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.39.0",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
|
||||
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/ncc": {
|
||||
"version": "0.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
|
||||
"integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"ncc": "dist/ncc/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"@actions/github": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
|
||||
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"requires": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"requires": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz",
|
||||
"integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==",
|
||||
"requires": {
|
||||
"@octokit/request": "^8.0.1",
|
||||
"@octokit/types": "^11.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz",
|
||||
"integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==",
|
||||
"requires": {
|
||||
"@octokit/types": "^11.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "18.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
|
||||
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz",
|
||||
"integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^9.0.0",
|
||||
"@octokit/request-error": "^5.0.0",
|
||||
"@octokit/types": "^11.1.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz",
|
||||
"integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==",
|
||||
"requires": {
|
||||
"@octokit/types": "^11.0.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz",
|
||||
"integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "12.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
|
||||
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
|
||||
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.40.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
|
||||
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.39.0",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
|
||||
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^12.11.0"
|
||||
}
|
||||
},
|
||||
"@vercel/ncc": {
|
||||
"version": "0.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
|
||||
"integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
|
||||
"dev": true
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
21
.github/actions/minus-one/package.json
vendored
Normal file
21
.github/actions/minus-one/package.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"private": true,
|
||||
"description": "Manage +1 comments on GitHub issues",
|
||||
"author": "Balázs Orbán <info@balazsorban.com>",
|
||||
"exports": "./index.mjs",
|
||||
"files": [
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "ncc -m -o . build src/index.mjs --license licenses.txt"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "0.34.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "1.10.0",
|
||||
"@actions/github": "5.1.1",
|
||||
"@octokit/graphql": "7.0.1"
|
||||
},
|
||||
"packageManager": "npm@9.2.0"
|
||||
}
|
||||
63
.github/actions/minus-one/src/index.mjs
vendored
Normal file
63
.github/actions/minus-one/src/index.mjs
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// @ts-check
|
||||
import { context } from '@actions/github'
|
||||
import { info, setFailed } from '@actions/core'
|
||||
import { graphql } from '@octokit/graphql'
|
||||
|
||||
if (!process.env.GITHUB_TOKEN) throw new Error('GITHUB_TOKEN not set')
|
||||
|
||||
/**
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus sindresorhus@gmail.com (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
function isUnhelpfulComment(text) {
|
||||
// Borrowed from Refined GitHub:
|
||||
// https://github.com/refined-github/refined-github/blob/c864a20b57bb433aaf3952f88d83c9fc481ae6ff/source/helpers/is-low-quality-comment.ts#L2-L3
|
||||
return (
|
||||
text.replace(
|
||||
/[\s,.!?👍👎👌🙏]+|[\u{1F3FB}-\u{1F3FF}]|[+-]\d+|⬆️|ditt?o|me|too|t?here|on|same|this|issues?|please|pl[sz]|any|updates?|bump|question|solution|following/giu,
|
||||
''
|
||||
) === ''
|
||||
)
|
||||
}
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const { comment } = context.payload
|
||||
if (!comment) return
|
||||
|
||||
const { node_id: subjectId, body } = comment
|
||||
|
||||
if (isUnhelpfulComment(body)) {
|
||||
await graphql(
|
||||
`
|
||||
mutation minimize($subjectId: ID!) {
|
||||
minimizeComment(
|
||||
input: { subjectId: $subjectId, classifier: OFF_TOPIC }
|
||||
) {
|
||||
minimizedComment {
|
||||
isMinimized
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
subjectId,
|
||||
headers: { authorization: `token ${process.env.GITHUB_TOKEN}` },
|
||||
}
|
||||
)
|
||||
|
||||
info(`Comment (${body.slice(0, 15)}) was minimized.`)
|
||||
}
|
||||
} catch (error) {
|
||||
setFailed(error)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
59
.github/workflows/ci-stable.yml
vendored
59
.github/workflows/ci-stable.yml
vendored
|
|
@ -1,59 +0,0 @@
|
|||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: CI — Stable
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master, 3.3]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: true
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
CACHE_PREFIX: stable
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14, 18]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Use the `stable` engine
|
||||
run: |
|
||||
node ./scripts/swap-engines.js
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
npm run test || \
|
||||
npm run test || \
|
||||
npm run test || exit 1
|
||||
|
||||
- name: Lint
|
||||
run: npm run style
|
||||
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
|
|
@ -1,10 +1,10 @@
|
|||
name: CI — Oxide
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, 3.3]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master, 3.3]
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -13,11 +13,11 @@ env:
|
|||
CI: true
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
CACHE_PREFIX: oxide
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
|
|
@ -62,14 +62,29 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Check versions
|
||||
run: |
|
||||
echo "Node:" `node --version`
|
||||
echo "NPM:" `npm --version`
|
||||
echo "Rust:" `rustc --version`
|
||||
echo "Cargo:" `cargo --version`
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npx turbo run build --filter=//
|
||||
|
||||
- name: Test
|
||||
- name: Test (Node.js)
|
||||
run: |
|
||||
npx turbo run test --filter=// || \
|
||||
npx turbo run test --filter=// || \
|
||||
npx turbo run test --filter=// || exit 1
|
||||
|
||||
- name: Test (Rust)
|
||||
run: |
|
||||
cd ./oxide
|
||||
cargo test || \
|
||||
cargo test || \
|
||||
cargo test || exit 1
|
||||
cd -
|
||||
|
||||
- name: Lint
|
||||
run: npx turbo run style --filter=//
|
||||
|
|
|
|||
83
.github/workflows/integration-tests-stable.yml
vendored
83
.github/workflows/integration-tests-stable.yml
vendored
|
|
@ -1,83 +0,0 @@
|
|||
name: Integration Tests — Stable
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, 3.3]
|
||||
pull_request:
|
||||
branches: [master, 3.3]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: true
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
CACHE_PREFIX: stable
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
integration:
|
||||
- content-resolution
|
||||
- parcel
|
||||
- postcss-cli
|
||||
- rollup
|
||||
- rollup-sass
|
||||
- tailwindcss-cli
|
||||
- vite
|
||||
- webpack-4
|
||||
- webpack-5
|
||||
node-version: [18]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
|
||||
- name: Use the `stable` engine
|
||||
run: |
|
||||
node ./scripts/swap-engines.js
|
||||
|
||||
- name: Cache node_modules (tailwindcss)
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
- name: Cache node_modules (integrations)
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./integrations/node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-integrations-${{ hashFiles('./integrations/package-lock.json') }}
|
||||
|
||||
- name: Cache node_modules (integration)
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./integrations/${{ matrix.integration }}/node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-integrations-${{ matrix.integration }}-${{ hashFiles('./integrations/${{ matrix.integration }}/package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Install dependencies (shared integrations)
|
||||
run: npm install --prefix ./integrations
|
||||
|
||||
- name: Install dependencies (integration)
|
||||
run: npm install --prefix ./integrations/${{ matrix.integration }}
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test ${{ matrix.integration }}
|
||||
run: |
|
||||
npm run test --prefix ./integrations/${{ matrix.integration }} || \
|
||||
npm run test --prefix ./integrations/${{ matrix.integration }} || \
|
||||
npm run test --prefix ./integrations/${{ matrix.integration }} || exit 1
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
name: Integration Tests — Oxide
|
||||
name: Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, 3.3]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master, 3.3]
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -13,11 +13,11 @@ env:
|
|||
CI: true
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
CACHE_PREFIX: oxide
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -46,7 +46,7 @@ jobs:
|
|||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
|
|
@ -74,6 +74,13 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Check versions
|
||||
run: |
|
||||
echo "Node:" `node --version`
|
||||
echo "NPM:" `npm --version`
|
||||
echo "Rust:" `rustc --version`
|
||||
echo "Cargo:" `cargo --version`
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npx turbo run build --filter=//
|
||||
|
||||
18
.github/workflows/issue_on_comment.yml
vendored
Normal file
18
.github/workflows/issue_on_comment.yml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
name: On issue comment
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
minus-one:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: 'Manage +1 comments'
|
||||
run: node ./.github/actions/minus-one/index.mjs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
8
.github/workflows/prepare-release.yml
vendored
8
.github/workflows/prepare-release.yml
vendored
|
|
@ -18,6 +18,7 @@ jobs:
|
|||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
|
||||
runs-on: macos-11
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -53,6 +54,13 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Check versions
|
||||
run: |
|
||||
echo "Node:" `node --version`
|
||||
echo "NPM:" `npm --version`
|
||||
echo "Rust:" `rustc --version`
|
||||
echo "Cargo:" `cargo --version`
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
|
|
|
|||
78
.github/workflows/release-insiders-stable.yml
vendored
78
.github/workflows/release-insiders-stable.yml
vendored
|
|
@ -1,78 +0,0 @@
|
|||
name: Release Insiders — Stable
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: true
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
CACHE_PREFIX: stable
|
||||
NODE_VERSION: 16
|
||||
RELEASE_CHANNEL: insiders
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Use the `stable` engine
|
||||
run: |
|
||||
node ./scripts/swap-engines.js
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-${{ env.CACHE_PREFIX }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
npm run test || \
|
||||
npm run test || \
|
||||
npm run test || exit 1
|
||||
|
||||
- name: Resolve version
|
||||
id: vars
|
||||
run: |
|
||||
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: 'Version based on commit: 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}'
|
||||
run: npm version 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }} --force --no-git-tag-version
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --tag ${{ env.RELEASE_CHANNEL }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Trigger Tailwind Play update
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }}
|
||||
script: |
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'tailwindlabs',
|
||||
repo: 'play.tailwindcss.com',
|
||||
ref: 'master',
|
||||
workflow_id: 'upgrade-tailwindcss.yml',
|
||||
inputs: {
|
||||
insidersVersion: '0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}'
|
||||
}
|
||||
})
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
name: Release Insiders — Oxide
|
||||
name: Release Insiders
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -8,7 +8,7 @@ env:
|
|||
APP_NAME: tailwindcss-oxide
|
||||
NODE_VERSION: 16
|
||||
OXIDE_LOCATION: ./oxide/crates/node
|
||||
RELEASE_CHANNEL: oxide-insiders
|
||||
RELEASE_CHANNEL: insiders
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -26,6 +26,7 @@ jobs:
|
|||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
|
@ -63,13 +64,6 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
|
|
@ -96,6 +90,7 @@ jobs:
|
|||
build-apple-silicon:
|
||||
name: Build Apple Silicon (OXIDE)
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
|
@ -133,13 +128,6 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
|
|
@ -170,24 +158,28 @@ jobs:
|
|||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
strip: strip
|
||||
image: docker.io/centos/nodejs-14-centos7
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
download: false
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
strip: aarch64-linux-gnu-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
setup: apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y
|
||||
strip: llvm-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
download: false
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
strip: arm-linux-gnueabihf-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
setup: apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
|
||||
strip: llvm-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
|
||||
download: false
|
||||
- target: aarch64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: aarch64-linux-musl-strip
|
||||
download: true
|
||||
- target: x86_64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: strip
|
||||
download: true
|
||||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
|
||||
|
|
@ -228,16 +220,10 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup cross compile toolchain
|
||||
if: ${{ matrix.setup }}
|
||||
run: ${{ matrix.setup }}
|
||||
- name: Install Rust (Stable)
|
||||
if: ${{ matrix.download }}
|
||||
run: |
|
||||
rustup default stable
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
|
@ -264,6 +250,7 @@ jobs:
|
|||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
name: Build and release
|
||||
needs:
|
||||
- build
|
||||
|
|
@ -353,8 +340,9 @@ jobs:
|
|||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
tailwind_oxide_release:
|
||||
tailwind-release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
name: Build and release Tailwind CSS
|
||||
|
||||
needs:
|
||||
332
.github/workflows/release-oxide.yml
vendored
332
.github/workflows/release-oxide.yml
vendored
|
|
@ -1,332 +0,0 @@
|
|||
name: Release — Oxide
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
APP_NAME: tailwindcss-oxide
|
||||
NODE_VERSION: 14
|
||||
OXIDE_LOCATION: ./oxide/crates/node
|
||||
|
||||
jobs:
|
||||
oxide-build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Windows
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
# Mac OS
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
strip: strip -x # Must use -x on macOS. This produces larger results on linux.
|
||||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
# - name: Use cached oxide node_modules
|
||||
# id: cache-oxide
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ env.OXIDE_LOCATION }}/node_modules
|
||||
# key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-${{ matrix.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}
|
||||
# restore-keys: |
|
||||
# nodeModules-
|
||||
|
||||
- name: Oxide — npm install
|
||||
# if: steps.cache-oxide.outputs.cache-hit != 'true'
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
if: ${{ matrix.strip }}
|
||||
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-${{ matrix.target }}
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ matrix.binary }}
|
||||
|
||||
oxide-build-apple-silicon:
|
||||
name: Build Apple Silicon (OXIDE)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('./oxide/**/Cargo.lock') }}
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: aarch64-apple-darwin
|
||||
JEMALLOC_SYS_WITH_LG_PAGE: 14
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
run: strip -x ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-aarch64-apple-darwin
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ env.APP_NAME }}
|
||||
|
||||
oxide-build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
strip: strip
|
||||
image: docker.io/centos/nodejs-14-centos7
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
strip: aarch64-linux-gnu-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
setup: apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
strip: arm-linux-gnueabihf-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
setup: apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
|
||||
- target: aarch64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: aarch64-linux-musl-strip
|
||||
- target: x86_64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: strip
|
||||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Setup cross compile toolchain
|
||||
if: ${{ matrix.setup }}
|
||||
run: ${{ matrix.setup }}
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
# - name: Use cached oxide node_modules
|
||||
# id: cache-oxide
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ env.OXIDE_LOCATION }}/node_modules
|
||||
# key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-linux-${{ matrix.target }}-${{ env.NODE_VERSION }}
|
||||
# restore-keys: |
|
||||
# nodeModules-
|
||||
|
||||
- name: Oxide — npm install
|
||||
# if: steps.cache-oxide.outputs.cache-hit != 'true'
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
if: ${{ matrix.strip }}
|
||||
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-${{ matrix.target }}
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ env.APP_NAME }}
|
||||
|
||||
oxide-release:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and release
|
||||
needs:
|
||||
- oxide-build
|
||||
- oxide-build-linux
|
||||
- oxide-build-apple-silicon
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# - name: Use cached oxide node_modules
|
||||
# id: cache-oxide
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ env.OXIDE_LOCATION }}/node_modules
|
||||
# key: nodeModules-${{ hashFiles('${{ env.OXIDE_LOCATION }}/package-lock.json') }}-ubuntu-latest-${{ env.NODE_VERSION }}
|
||||
# restore-keys: |
|
||||
# nodeModules-
|
||||
|
||||
- name: Oxide — npm install
|
||||
# if: steps.cache-oxide.outputs.cache-hit != 'true'
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm install
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: ${{ env.OXIDE_LOCATION }}
|
||||
|
||||
- run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > ~/.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Move artifacts
|
||||
run: |
|
||||
cd ${{ env.OXIDE_LOCATION }}
|
||||
cp bindings-aarch64-apple-darwin/oxide/crates/node/* ./npm/darwin-arm64/
|
||||
cp bindings-aarch64-unknown-linux-gnu/oxide/crates/node/* ./npm/linux-arm64-gnu/
|
||||
cp bindings-aarch64-unknown-linux-musl/oxide/crates/node/* ./npm/linux-arm64-musl/
|
||||
cp bindings-armv7-unknown-linux-gnueabihf/oxide/crates/node/* ./npm/linux-arm-gnueabihf/
|
||||
cp bindings-x86_64-apple-darwin/* ./npm/darwin-x64/
|
||||
cp bindings-x86_64-pc-windows-msvc/* ./npm/win32-x64-msvc/
|
||||
cp bindings-x86_64-unknown-linux-gnu/oxide/crates/node/* ./npm/linux-x64-gnu/
|
||||
cp bindings-x86_64-unknown-linux-musl/oxide/crates/node/* ./npm/linux-x64-musl/
|
||||
|
||||
- name: Resolve version
|
||||
id: vars
|
||||
run: |
|
||||
echo "NPM_VERSION=0.0.0-insiders.$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate entry point
|
||||
run: npm run build --prefix ${{ env.OXIDE_LOCATION }}
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
for pkg in ${{ env.OXIDE_LOCATION }}/npm/*; do
|
||||
echo "Publishing $pkg..."
|
||||
cd $pkg
|
||||
npm version ${{ env.NPM_VERSION }} --force --no-git-tag-version
|
||||
npm publish --tag insiders --access public
|
||||
cd -
|
||||
done
|
||||
|
||||
echo "Publishing ${{ env.APP_NAME }}...";
|
||||
cd ${{ env.OXIDE_LOCATION }}
|
||||
npm version ${{ env.NPM_VERSION }} --force --no-git-tag-version
|
||||
sed "s#\"0.0.0\"#\"${{ env.NPM_VERSION }}\"#g" package.json > package_updated.json
|
||||
mv package_updated.json package.json
|
||||
npm publish --tag insiders --access public
|
||||
cd -
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
tailwind_oxide_release:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and release Tailwind CSS
|
||||
|
||||
needs:
|
||||
- oxide-release
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Resolve version
|
||||
id: vars
|
||||
run: |
|
||||
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "OXIDE_NPM_VERSION=0.0.0-insiders.$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup `@tailwindcss/oxide` version
|
||||
run: |
|
||||
sed "s#\"@tailwindcss/oxide\": \".*\"#\"@tailwindcss/oxide\": \"${{ env.OXIDE_NPM_VERSION }}\"#" package.json > package_updated.json
|
||||
mv package_updated.json package.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: 'Version based on commit: 0.0.0-oxide.${{ env.SHA_SHORT }}'
|
||||
run: npm version 0.0.0-oxide.${{ env.SHA_SHORT }} --force --no-git-tag-version
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --tag oxide
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
68
.github/workflows/release-stable.yml
vendored
68
.github/workflows/release-stable.yml
vendored
|
|
@ -1,68 +0,0 @@
|
|||
name: Release — Stable
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Use the `stable` engine
|
||||
run: |
|
||||
node ./scripts/swap-engines.js
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm run test
|
||||
|
||||
- name: Calculate environment variables
|
||||
run: |
|
||||
echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV
|
||||
echo "TAILWINDCSS_VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --tag ${{ env.RELEASE_CHANNEL }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Trigger Tailwind Play update
|
||||
if: env.RELEASE_CHANNEL == 'latest'
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }}
|
||||
script: |
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'tailwindlabs',
|
||||
repo: 'play.tailwindcss.com',
|
||||
ref: 'master',
|
||||
workflow_id: 'upgrade-tailwindcss.yml',
|
||||
inputs: {
|
||||
version: '${{ env.TAILWINDCSS_VERSION }}'
|
||||
}
|
||||
})
|
||||
429
.github/workflows/release.yml
vendored
Normal file
429
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
APP_NAME: tailwindcss-oxide
|
||||
NODE_VERSION: 16
|
||||
OXIDE_LOCATION: ./oxide/crates/node
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Windows
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
# Mac OS
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
strip: strip -x # Must use -x on macOS. This produces larger results on linux.
|
||||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Cache the `oxide` Rust build
|
||||
- name: Cache oxide build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
./oxide/target/
|
||||
./oxide/crates/node/*.node
|
||||
./oxide/crates/node/index.js
|
||||
./oxide/crates/node/index.d.ts
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }}
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
if: ${{ matrix.strip }}
|
||||
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-${{ matrix.target }}
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ matrix.binary }}
|
||||
|
||||
build-apple-silicon:
|
||||
name: Build Apple Silicon (OXIDE)
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Cache the `oxide` Rust build
|
||||
- name: Cache oxide build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
./oxide/target/
|
||||
./oxide/crates/node/*.node
|
||||
./oxide/crates/node/index.js
|
||||
./oxide/crates/node/index.d.ts
|
||||
key: ${{ runner.os }}-oxide-${{ hashFiles('./oxide/crates/**/*') }}
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: aarch64-apple-darwin
|
||||
JEMALLOC_SYS_WITH_LG_PAGE: 14
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
run: strip -x ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-aarch64-apple-darwin
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ env.APP_NAME }}
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
strip: strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
strip: llvm-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
strip: llvm-strip
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
|
||||
- target: aarch64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: aarch64-linux-musl-strip
|
||||
- target: x86_64-unknown-linux-musl
|
||||
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
|
||||
strip: strip
|
||||
|
||||
name: Build ${{ matrix.target }} (OXIDE)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Cache the `oxide` Rust build
|
||||
- name: Cache oxide build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
./oxide/target/
|
||||
./oxide/crates/node/*.node
|
||||
./oxide/crates/node/index.js
|
||||
./oxide/crates/node/index.d.ts
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }}
|
||||
|
||||
- name: Install Node.JS
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Setup cross compile toolchain
|
||||
if: ${{ matrix.setup }}
|
||||
run: ${{ matrix.setup }}
|
||||
|
||||
- name: Setup rust target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build release
|
||||
run: cd ${{ env.OXIDE_LOCATION }} && npm run build
|
||||
env:
|
||||
RUST_TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
|
||||
if: ${{ matrix.strip }}
|
||||
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bindings-${{ matrix.target }}
|
||||
path: |
|
||||
${{ env.OXIDE_LOCATION }}/*.node
|
||||
${{ env.APP_NAME }}
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
name: Build and release
|
||||
needs:
|
||||
- build
|
||||
- build-linux
|
||||
- build-apple-silicon
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Cache the `oxide` Rust build
|
||||
- name: Cache oxide build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
./oxide/target/
|
||||
./oxide/crates/node/*.node
|
||||
./oxide/crates/node/index.js
|
||||
./oxide/crates/node/index.d.ts
|
||||
key: ${{ runner.os }}-oxide-${{ hashFiles('./oxide/crates/**/*') }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Calculate environment variables
|
||||
run: |
|
||||
echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV
|
||||
echo "VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: ${{ env.OXIDE_LOCATION }}
|
||||
|
||||
- run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > ~/.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Move artifacts
|
||||
run: |
|
||||
cd ${{ env.OXIDE_LOCATION }}
|
||||
cp bindings-aarch64-apple-darwin/oxide/crates/node/* ./npm/darwin-arm64/
|
||||
cp bindings-aarch64-unknown-linux-gnu/oxide/crates/node/* ./npm/linux-arm64-gnu/
|
||||
cp bindings-aarch64-unknown-linux-musl/oxide/crates/node/* ./npm/linux-arm64-musl/
|
||||
cp bindings-armv7-unknown-linux-gnueabihf/oxide/crates/node/* ./npm/linux-arm-gnueabihf/
|
||||
cp bindings-x86_64-apple-darwin/* ./npm/darwin-x64/
|
||||
cp bindings-x86_64-pc-windows-msvc/* ./npm/win32-x64-msvc/
|
||||
cp bindings-x86_64-unknown-linux-gnu/oxide/crates/node/* ./npm/linux-x64-gnu/
|
||||
cp bindings-x86_64-unknown-linux-musl/oxide/crates/node/* ./npm/linux-x64-musl/
|
||||
|
||||
- name: Generate entry point
|
||||
run: npm run build --prefix ${{ env.OXIDE_LOCATION }}
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
for pkg in ${{ env.OXIDE_LOCATION }}/npm/*; do
|
||||
echo "Publishing $pkg..."
|
||||
cd $pkg
|
||||
npm version ${{ env.VERSION }} --force --no-git-tag-version
|
||||
npm publish --tag ${{ env.RELEASE_CHANNEL }} --access public
|
||||
cd -
|
||||
done
|
||||
|
||||
echo "Publishing ${{ env.APP_NAME }}...";
|
||||
cd ${{ env.OXIDE_LOCATION }}
|
||||
npm version ${{ env.VERSION }} --force --no-git-tag-version
|
||||
sed "s#\"0.0.0\"#\"${{ env.VERSION }}\"#g" package.json > package_updated.json
|
||||
mv package_updated.json package.json
|
||||
npm publish --tag ${{ env.RELEASE_CHANNEL }} --access public
|
||||
cd -
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
tailwind-release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
name: Build and release Tailwind CSS
|
||||
|
||||
needs:
|
||||
- release
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-${{ env.NODE_VERSION }}-node_modules-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
# Cargo already skips downloading dependencies if they already exist
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Cache the `oxide` Rust build
|
||||
- name: Cache oxide build
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
./oxide/target/
|
||||
./oxide/crates/node/*.node
|
||||
./oxide/crates/node/index.js
|
||||
./oxide/crates/node/index.d.ts
|
||||
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./oxide/crates/**/*') }}
|
||||
|
||||
- name: Use Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Calculate environment variables
|
||||
run: |
|
||||
echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV
|
||||
echo "VERSION=$(node -e 'console.log(require(`./package.json`).version);')" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup `@tailwindcss/oxide` version
|
||||
run: |
|
||||
sed "s#\"@tailwindcss/oxide\": \".*\"#\"@tailwindcss/oxide\": \"${{ env.VERSION }}\"#" package.json > package_updated.json
|
||||
mv package_updated.json package.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Tailwind CSS
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
npm test || \
|
||||
npm test || \
|
||||
npm test || exit 1
|
||||
|
||||
- name: 'Version: ${{ env.VERSION }}'
|
||||
run: npm version ${{ env.VERSION }} --force --no-git-tag-version
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --tag ${{ env.RELEASE_CHANNEL }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
# - name: Trigger Tailwind Play update
|
||||
# if: env.RELEASE_CHANNEL == 'latest'
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }}
|
||||
# script: |
|
||||
# await github.rest.actions.createWorkflowDispatch({
|
||||
# owner: 'tailwindlabs',
|
||||
# repo: 'play.tailwindcss.com',
|
||||
# ref: 'master',
|
||||
# workflow_id: 'upgrade-tailwindcss.yml',
|
||||
# inputs: {
|
||||
# version: '${{ env.TAILWINDCSS_VERSION }}'
|
||||
# }
|
||||
# })
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
/node_modules
|
||||
.github/**/node_modules
|
||||
/coverage
|
||||
/cli
|
||||
/lib
|
||||
|
|
|
|||
6
.swcrc
6
.swcrc
|
|
@ -2,12 +2,8 @@
|
|||
"module": {
|
||||
"type": "commonjs"
|
||||
},
|
||||
"env": {
|
||||
"targets": {
|
||||
"node": "12.13.0"
|
||||
}
|
||||
},
|
||||
"jsc": {
|
||||
"target": "es2018",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": false
|
||||
|
|
|
|||
104
CHANGELOG.md
104
CHANGELOG.md
|
|
@ -7,7 +7,104 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- Nothing yet!
|
||||
### Fixed
|
||||
|
||||
- Improve types for `resolveConfig` ([#12272](https://github.com/tailwindlabs/tailwindcss/pull/12272))
|
||||
- Don’t add spaces to negative numbers following a comma ([#12324](https://github.com/tailwindlabs/tailwindcss/pull/12324))
|
||||
- Don't emit `@config` in CSS when watching via the CLI ([#12327](https://github.com/tailwindlabs/tailwindcss/pull/12327))
|
||||
- Ensure configured `font-feature-settings` for `mono` are included in Preflight ([#12342](https://github.com/tailwindlabs/tailwindcss/pull/12342))
|
||||
- Improve candidate detection in minified JS arrays (without spaces) ([#12396](https://github.com/tailwindlabs/tailwindcss/pull/12396))
|
||||
- Don't crash when given applying a variant to a negated version of a simple utility ([#12514](https://github.com/tailwindlabs/tailwindcss/pull/12514))
|
||||
- Fix support for slashes in arbitrary modifiers ([#12515](https://github.com/tailwindlabs/tailwindcss/pull/12515))
|
||||
- Fix source maps of variant utilities that come from an `@layer` rule ([#12508](https://github.com/tailwindlabs/tailwindcss/pull/12508))
|
||||
- Fix loading of built-in plugins when using an ESM or TypeScript config with the Standalone CLI ([#12506](https://github.com/tailwindlabs/tailwindcss/pull/12506))
|
||||
- [Oxide] Remove `autoprefixer` dependency ([#11315](https://github.com/tailwindlabs/tailwindcss/pull/11315))
|
||||
- [Oxide] Fix source maps issue resulting in a crash ([#11319](https://github.com/tailwindlabs/tailwindcss/pull/11319))
|
||||
- [Oxide] Fallback to RegEx based parser when using custom transformers or extractors ([#11335](https://github.com/tailwindlabs/tailwindcss/pull/11335))
|
||||
- [Oxide] Bump `lightningcss` and reflect related improvements in tests ([#11550](https://github.com/tailwindlabs/tailwindcss/pull/11550))
|
||||
|
||||
### Added
|
||||
|
||||
- Add `svh`, `lvh`, and `dvh` values to default `height`/`min-height`/`max-height` theme ([#11317](https://github.com/tailwindlabs/tailwindcss/pull/11317))
|
||||
- Add `has-*` variants for `:has(...)` pseudo-class ([#11318](https://github.com/tailwindlabs/tailwindcss/pull/11318))
|
||||
- Add `text-wrap` utilities including `text-balance` and `text-pretty` ([#11320](https://github.com/tailwindlabs/tailwindcss/pull/11320), [#12031](https://github.com/tailwindlabs/tailwindcss/pull/12031))
|
||||
- Extend default `opacity` scale to include all steps of 5 ([#11832](https://github.com/tailwindlabs/tailwindcss/pull/11832))
|
||||
- Update Preflight `html` styles to include shadow DOM `:host` pseudo-class ([#11200](https://github.com/tailwindlabs/tailwindcss/pull/11200))
|
||||
- Increase default values for `grid-rows-*` utilities from 1–6 to 1–12 ([#12180](https://github.com/tailwindlabs/tailwindcss/pull/12180))
|
||||
- Add `size-*` utilities ([#12287](https://github.com/tailwindlabs/tailwindcss/pull/12287))
|
||||
- Add utilities for CSS subgrid ([#12298](https://github.com/tailwindlabs/tailwindcss/pull/12298))
|
||||
- Add spacing scale to `min-w-*`, `min-h-*`, and `max-w-*` utilities ([#12300](https://github.com/tailwindlabs/tailwindcss/pull/12300))
|
||||
- Add `forced-color-adjust` utilities ([#11931](https://github.com/tailwindlabs/tailwindcss/pull/11931))
|
||||
- Add `forced-colors` variant ([#11694](https://github.com/tailwindlabs/tailwindcss/pull/11694))
|
||||
- Add `appearance-auto` utility ([#12404](https://github.com/tailwindlabs/tailwindcss/pull/12404))
|
||||
- Add logical property values for `float` and `clear` utilities ([#12480](https://github.com/tailwindlabs/tailwindcss/pull/12480))
|
||||
- [Oxide] New Rust template parsing engine ([#10252](https://github.com/tailwindlabs/tailwindcss/pull/10252))
|
||||
- [Oxide] Support `@import "tailwindcss"` using top-level `index.css` file ([#11205](https://github.com/tailwindlabs/tailwindcss/pull/11205), ([#11260](https://github.com/tailwindlabs/tailwindcss/pull/11260)))
|
||||
- [Oxide] Use `lightningcss` for nesting and vendor prefixes in PostCSS plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399))
|
||||
- [Oxide] Automatically detect content paths when no `content` configuration is provided ([#11173](https://github.com/tailwindlabs/tailwindcss/pull/11173), [#11221](https://github.com/tailwindlabs/tailwindcss/pull/11221))
|
||||
- [Oxide] Process and inline `@import` at-rules natively ([#11239](https://github.com/tailwindlabs/tailwindcss/pull/11239))
|
||||
- [Oxide] Explicitly configure Lightning CSS features, and prefer user browserslist over default browserslist ([#11402](https://github.com/tailwindlabs/tailwindcss/pull/11402), [#11412](https://github.com/tailwindlabs/tailwindcss/pull/11412))
|
||||
- [Oxide] Support loading plugins by package / file name ([#12087](https://github.com/tailwindlabs/tailwindcss/pull/12087))
|
||||
|
||||
### Changed
|
||||
|
||||
- Simplify the `sans` font-family stack ([#11748](https://github.com/tailwindlabs/tailwindcss/pull/11748))
|
||||
- Disable the tap highlight overlay on iOS ([#12299](https://github.com/tailwindlabs/tailwindcss/pull/12299))
|
||||
- [Oxide] Deprecate `--no-autoprefixer` flag in the CLI ([#11280](https://github.com/tailwindlabs/tailwindcss/pull/11280))
|
||||
- [Oxide] Make the Rust based parser the default ([#11394](https://github.com/tailwindlabs/tailwindcss/pull/11394))
|
||||
|
||||
## [3.3.5] - 2023-10-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix incorrect spaces around `-` in `calc()` expression ([#12283](https://github.com/tailwindlabs/tailwindcss/pull/12283))
|
||||
|
||||
## [3.3.4] - 2023-10-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve normalisation of `calc()`-like functions ([#11686](https://github.com/tailwindlabs/tailwindcss/pull/11686))
|
||||
- Skip `calc()` normalisation in nested `theme()` calls ([#11705](https://github.com/tailwindlabs/tailwindcss/pull/11705))
|
||||
- Fix incorrectly generated CSS when using square brackets inside arbitrary properties ([#11709](https://github.com/tailwindlabs/tailwindcss/pull/11709))
|
||||
- Make `content` optional for presets in TypeScript types ([#11730](https://github.com/tailwindlabs/tailwindcss/pull/11730))
|
||||
- Handle variable colors that have variable fallback values ([#12049](https://github.com/tailwindlabs/tailwindcss/pull/12049))
|
||||
- Batch reading content files to prevent `too many open files` error ([#12079](https://github.com/tailwindlabs/tailwindcss/pull/12079))
|
||||
- Skip over classes inside `:not(…)` when nested in an at-rule ([#12105](https://github.com/tailwindlabs/tailwindcss/pull/12105))
|
||||
- Update types to work with `Node16` module resolution ([#12097](https://github.com/tailwindlabs/tailwindcss/pull/12097))
|
||||
- Don’t crash when important and parent selectors are equal in `@apply` ([#12112](https://github.com/tailwindlabs/tailwindcss/pull/12112))
|
||||
- Eliminate irrelevant rules when applying variants ([#12113](https://github.com/tailwindlabs/tailwindcss/pull/12113))
|
||||
- Improve RegEx parser, reduce possibilities as the key for arbitrary properties ([#12121](https://github.com/tailwindlabs/tailwindcss/pull/12121))
|
||||
- Fix sorting of utilities that share multiple candidates ([#12173](https://github.com/tailwindlabs/tailwindcss/pull/12173))
|
||||
- Ensure variants with arbitrary values and a modifier are correctly matched in the RegEx based parser ([#12179](https://github.com/tailwindlabs/tailwindcss/pull/12179))
|
||||
- Fix crash when watching renamed files on FreeBSD ([#12193](https://github.com/tailwindlabs/tailwindcss/pull/12193))
|
||||
- Allow plugins from a parent document to be used in an iframe ([#12208](https://github.com/tailwindlabs/tailwindcss/pull/12208))
|
||||
- Add types for `tailwindcss/nesting` ([#12269](https://github.com/tailwindlabs/tailwindcss/pull/12269))
|
||||
- Bump `jiti`, `fast-glob`, and `browserlist` dependencies ([#11550](https://github.com/tailwindlabs/tailwindcss/pull/11550))
|
||||
- Improve automatic `var` injection for properties that accept a `<dashed-ident>` ([#12236](https://github.com/tailwindlabs/tailwindcss/pull/12236))
|
||||
|
||||
## [3.3.3] - 2023-07-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix issue where some pseudo-element variants generated the wrong selector ([#10943](https://github.com/tailwindlabs/tailwindcss/pull/10943), [#10962](https://github.com/tailwindlabs/tailwindcss/pull/10962), [#11111](https://github.com/tailwindlabs/tailwindcss/pull/11111))
|
||||
- Make font settings propagate into buttons, inputs, etc. ([#10940](https://github.com/tailwindlabs/tailwindcss/pull/10940))
|
||||
- Fix parsing of `theme()` inside `calc()` when there are no spaces around operators ([#11157](https://github.com/tailwindlabs/tailwindcss/pull/11157))
|
||||
- Ensure `repeating-conic-gradient` is detected as an image ([#11180](https://github.com/tailwindlabs/tailwindcss/pull/11180))
|
||||
- Move unknown pseudo-elements outside of `:is` by default ([#11345](https://github.com/tailwindlabs/tailwindcss/pull/11345))
|
||||
- Escape animation names when prefixes contain special characters ([#11470](https://github.com/tailwindlabs/tailwindcss/pull/11470))
|
||||
- Don't prefix arbitrary classes in `group` and `peer` variants ([#11454](https://github.com/tailwindlabs/tailwindcss/pull/11454))
|
||||
- Sort classes using position of first matching rule ([#11504](https://github.com/tailwindlabs/tailwindcss/pull/11504))
|
||||
- Allow variant to be an at-rule without a prelude ([#11589](https://github.com/tailwindlabs/tailwindcss/pull/11589))
|
||||
- Make PostCSS plugin async to improve performance ([#11548](https://github.com/tailwindlabs/tailwindcss/pull/11548))
|
||||
- Don’t error when a config file is missing ([f97759f](https://github.com/tailwindlabs/tailwindcss/commit/f97759f808d15ace66647b1405744fcf95a392e5))
|
||||
|
||||
### Added
|
||||
|
||||
- Add `aria-busy` utility ([#10966](https://github.com/tailwindlabs/tailwindcss/pull/10966))
|
||||
|
||||
### Changed
|
||||
|
||||
- Reset padding for `<dialog>` elements in preflight ([#11069](https://github.com/tailwindlabs/tailwindcss/pull/11069))
|
||||
|
||||
## [3.3.2] - 2023-04-25
|
||||
|
||||
|
|
@ -2236,7 +2333,10 @@ No release notes
|
|||
|
||||
- Everything!
|
||||
|
||||
[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.2...HEAD
|
||||
[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.5...HEAD
|
||||
[3.3.5]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.4...v3.3.5
|
||||
[3.3.4]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.3...v3.3.4
|
||||
[3.3.3]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.2...v3.3.3
|
||||
[3.3.2]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.1...v3.3.2
|
||||
[3.3.1]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.0...v3.3.1
|
||||
[3.3.0]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.7...v3.3.0
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci-stable.yml?branch=master" alt="Build Status"></a>
|
||||
<a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci.yml?branch=master" alt="Build Status"></a>
|
||||
<a href="https://www.npmjs.com/package/tailwindcss"><img src="https://img.shields.io/npm/dt/tailwindcss.svg" alt="Total Downloads"></a>
|
||||
<a href="https://github.com/tailwindcss/tailwindcss/releases"><img src="https://img.shields.io/npm/v/tailwindcss.svg" alt="Latest Release"></a>
|
||||
<a href="https://github.com/tailwindcss/tailwindcss/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/tailwindcss.svg" alt="License"></a>
|
||||
|
|
|
|||
5
index.css
Normal file
5
index.css
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@tailwind base;
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "^8.4.23",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-cli": "^10.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ it('looks in the CWD by default', async () => {
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-static-positive\] {
|
||||
--tw-content: real-static-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -69,7 +69,7 @@ it('looks in the CWD by default', async () => {
|
|||
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
})
|
||||
|
||||
it('looks in the CWD for non-config-relative paths', async () => {
|
||||
|
|
@ -88,7 +88,7 @@ it('looks in the CWD for non-config-relative paths', async () => {
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-static-positive\] {
|
||||
--tw-content: real-static-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -97,7 +97,7 @@ it('looks in the CWD for non-config-relative paths', async () => {
|
|||
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
})
|
||||
|
||||
it('can look for content files relative to the config', async () => {
|
||||
|
|
@ -113,12 +113,12 @@ it('can look for content files relative to the config', async () => {
|
|||
// Here `./real` doesn't exist next to the config in the root directory
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(css``)
|
||||
expect(result.css).toMatchFormattedCss(css``)
|
||||
|
||||
// But here it `./real` does exist next to the config in the `./src` directory!
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-static-positive\] {
|
||||
--tw-content: real-static-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -143,7 +143,7 @@ it('it handles ignored globs correctly when not relative to the config', async (
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-dynamic-positive\] {
|
||||
--tw-content: real-dynamic-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -157,7 +157,7 @@ it('it handles ignored globs correctly when not relative to the config', async (
|
|||
// But here it `./real` does exist next to the config in the `./src` directory!
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
})
|
||||
|
||||
it('it handles ignored globs correctly when relative to the config', async () => {
|
||||
|
|
@ -177,12 +177,12 @@ it('it handles ignored globs correctly when relative to the config', async () =>
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
|
||||
// But here it `./real` does exist next to the config in the `./src` directory!
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-dynamic-positive\] {
|
||||
--tw-content: real-dynamic-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -215,7 +215,7 @@ it('it can resolve symlinks for files when not relative to the config', async ()
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-dynamic-positive\] {
|
||||
--tw-content: real-dynamic-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
@ -237,7 +237,7 @@ it('it can resolve symlinks for files when not relative to the config', async ()
|
|||
// But here it `./real` does exist next to the config in the `./src` directory!
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
})
|
||||
|
||||
it('it can resolve symlinks for files when relative to the config', async () => {
|
||||
|
|
@ -261,12 +261,12 @@ it('it can resolve symlinks for files when relative to the config', async () =>
|
|||
|
||||
let result = await build({ cwd: path.resolve(__dirname, '..') })
|
||||
|
||||
expect(result.css).toMatchCss(``)
|
||||
expect(result.css).toMatchFormattedCss(``)
|
||||
|
||||
// But here it `./real` does exist next to the config in the `./src` directory!
|
||||
result = await build({ cwd: path.resolve(__dirname, '../src') })
|
||||
|
||||
expect(result.css).toMatchCss(css`
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.content-\[real-dynamic-positive\] {
|
||||
--tw-content: real-dynamic-positive;
|
||||
content: var(--tw-content);
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ let resolveToolRoot = require('./resolve-tool-root')
|
|||
|
||||
let SHOW_OUTPUT = false
|
||||
|
||||
let runningProcessess = []
|
||||
let runningProcesses = []
|
||||
|
||||
afterEach(() => {
|
||||
runningProcessess.splice(0).forEach((runningProcess) => runningProcess.stop())
|
||||
runningProcesses.splice(0).forEach((runningProcess) => runningProcess.stop())
|
||||
})
|
||||
|
||||
function debounce(fn, ms) {
|
||||
|
|
@ -64,7 +64,7 @@ module.exports = function $(command, options = {}) {
|
|||
messages.splice(0, idx + 1)
|
||||
let actorIdx = actors.indexOf(next)
|
||||
actors.splice(actorIdx, 1)
|
||||
next.resolve()
|
||||
next.resolve(message)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -129,7 +129,7 @@ module.exports = function $(command, options = {}) {
|
|||
})
|
||||
})
|
||||
|
||||
runningProcessess.push(runningProcess)
|
||||
runningProcesses.push(runningProcess)
|
||||
|
||||
return Object.assign(runningProcess, {
|
||||
stop() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ let fs = require('fs/promises')
|
|||
let chokidar = require('chokidar')
|
||||
|
||||
let resolveToolRoot = require('./resolve-tool-root')
|
||||
let FILE_STATE = {
|
||||
NotFound: Symbol(),
|
||||
}
|
||||
|
||||
function getWatcherOptions() {
|
||||
return {
|
||||
|
|
@ -28,7 +31,7 @@ module.exports = function ({
|
|||
cleanup = true,
|
||||
} = {}) {
|
||||
let toolRoot = resolveToolRoot()
|
||||
let fileCache = {}
|
||||
let fileCache = new Map()
|
||||
|
||||
let absoluteOutputFolder = path.resolve(toolRoot, output)
|
||||
let absoluteInputFolder = path.resolve(toolRoot, input)
|
||||
|
|
@ -41,9 +44,9 @@ module.exports = function ({
|
|||
// Restore all written files
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
Object.entries(fileCache).map(async ([file, content]) => {
|
||||
Array.from(fileCache.entries()).map(async ([file, content]) => {
|
||||
try {
|
||||
if (content === null) {
|
||||
if (content === FILE_STATE.NotFound) {
|
||||
return await fs.unlink(file)
|
||||
} else {
|
||||
return await fs.writeFile(file, content, 'utf8')
|
||||
|
|
@ -89,7 +92,7 @@ module.exports = function ({
|
|||
return {
|
||||
cleanupFile(file) {
|
||||
let filePath = path.resolve(toolRoot, file)
|
||||
fileCache[filePath] = null
|
||||
fileCache.set(filePath, FILE_STATE.NotFound)
|
||||
},
|
||||
async fileExists(file) {
|
||||
let filePath = path.resolve(toolRoot, file)
|
||||
|
|
@ -98,8 +101,11 @@ module.exports = function ({
|
|||
async removeFile(file) {
|
||||
let filePath = path.resolve(toolRoot, file)
|
||||
|
||||
if (!fileCache[filePath]) {
|
||||
fileCache[filePath] = await fs.readFile(filePath, 'utf8').catch(() => null)
|
||||
if (!fileCache.has(filePath)) {
|
||||
fileCache.set(
|
||||
filePath,
|
||||
await fs.readFile(filePath, 'utf8').catch(() => FILE_STATE.NotFound)
|
||||
)
|
||||
}
|
||||
|
||||
await fs.unlink(filePath).catch(() => null)
|
||||
|
|
@ -114,20 +120,31 @@ module.exports = function ({
|
|||
},
|
||||
async appendToInputFile(file, contents) {
|
||||
let filePath = path.resolve(absoluteInputFolder, file)
|
||||
if (!fileCache[filePath]) {
|
||||
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
|
||||
if (!fileCache.has(filePath)) {
|
||||
fileCache.set(filePath, await fs.readFile(filePath, 'utf8'))
|
||||
}
|
||||
|
||||
return fs.appendFile(filePath, contents, 'utf8')
|
||||
},
|
||||
async writeInputFile(file, contents) {
|
||||
let filePath = path.resolve(absoluteInputFolder, file)
|
||||
if (!fileCache[filePath]) {
|
||||
|
||||
// Ensure the parent folder of the file exists
|
||||
if (
|
||||
!(await fs
|
||||
.stat(filePath)
|
||||
.then(() => true)
|
||||
.catch(() => false))
|
||||
) {
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
||||
}
|
||||
|
||||
if (!fileCache.has(filePath)) {
|
||||
try {
|
||||
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
|
||||
fileCache.set(filePath, await fs.readFile(filePath, 'utf8'))
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
fileCache[filePath] = null // Sentinel value to `delete` the file afterwards. This also means that we are writing to a `new` file inside the test.
|
||||
fileCache.set(filePath, FILE_STATE.NotFound)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"parcel": "^2.8.3"
|
||||
"parcel": "^2.10.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let {
|
||||
appendToInputFile,
|
||||
|
|
@ -40,7 +39,7 @@ describe('static build', () => {
|
|||
'index.html',
|
||||
html`
|
||||
<link rel="stylesheet" href="./index.css" />
|
||||
<div class="bg-primary"></div>
|
||||
<div class="z-primary"></div>
|
||||
`
|
||||
)
|
||||
await writeInputFile(
|
||||
|
|
@ -58,8 +57,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -74,26 +73,13 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
it('can use a tailwind.config.ts configuration file', async () => {
|
||||
|
|
@ -102,7 +88,7 @@ describe('static build', () => {
|
|||
'index.html',
|
||||
html`
|
||||
<link rel="stylesheet" href="./index.css" />
|
||||
<div class="bg-primary"></div>
|
||||
<div class="z-primary"></div>
|
||||
`
|
||||
)
|
||||
await writeInputFile(
|
||||
|
|
@ -122,8 +108,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -138,26 +124,13 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -199,41 +172,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange(/index\.\w+\.css$/, async () => {
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -271,41 +225,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange(/index\.\w+\.css$/, async () => {
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -328,7 +263,7 @@ describe('watcher', () => {
|
|||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
@media (width >= 768px) {
|
||||
.md\:font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -366,7 +301,7 @@ describe('watcher', () => {
|
|||
.font-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
@media (min-width: 800px) {
|
||||
@media (width >= 800px) {
|
||||
.md\:font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -417,11 +352,11 @@ describe('watcher', () => {
|
|||
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
/* prettier-ignore */
|
||||
.btn {
|
||||
border-radius: .25rem;
|
||||
padding: .25rem .5rem;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
|
@ -438,43 +373,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
background-color: #ef4444;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index\.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "^8.4.23",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-cli": "^10.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({
|
||||
output: 'dist',
|
||||
|
|
@ -58,41 +57,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -125,41 +105,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -187,23 +148,23 @@ describe('watcher', () => {
|
|||
await writeInputFile(
|
||||
'../tailwind.config.js',
|
||||
javascript`
|
||||
module.exports = {
|
||||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
screens: {
|
||||
md: '800px'
|
||||
},
|
||||
fontWeight: {
|
||||
bold: 'bold'
|
||||
}
|
||||
module.exports = {
|
||||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
screens: {
|
||||
md: '800px'
|
||||
},
|
||||
fontWeight: {
|
||||
bold: 'bold'
|
||||
}
|
||||
},
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
},
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
|
@ -277,47 +238,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
/* prettier-ignore */
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
background-color: #ef4444;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup": "^3.21.0",
|
||||
"rollup": "^4.5.0",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"sass": "^1.62.0"
|
||||
"sass": "^1.69.5"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let { readOutputFile, appendToInputFile, writeInputFile } = require('../../io')({
|
||||
output: 'dist',
|
||||
|
|
@ -58,41 +57,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -125,41 +105,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -275,43 +236,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
background-color: #ef4444;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -352,6 +295,8 @@ describe('watcher', () => {
|
|||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
|
|
@ -370,44 +315,26 @@ describe('watcher', () => {
|
|||
css`
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
background-color: #ef4444;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
}, 30000)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup": "^3.21.0",
|
||||
"rollup": "^4.5.0",
|
||||
"rollup-plugin-postcss": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let { readOutputFile, appendToInputFile, writeInputFile, removeFile } = require('../../io')({
|
||||
output: 'dist',
|
||||
|
|
@ -30,7 +29,7 @@ describe('static build', () => {
|
|||
|
||||
it('can use a tailwind.config.js configuration file with ESM syntax', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -46,8 +45,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -62,31 +61,18 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
it('can use a tailwind.config.ts configuration file', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -104,8 +90,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -120,26 +106,13 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -172,41 +145,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -239,41 +193,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -389,46 +324,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStderr(ready)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
background-color: #ef4444;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('index.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
@ -0,0 +1 @@
|
|||
<div class="font-bold"></div>
|
||||
|
|
@ -2,7 +2,6 @@ let path = require('path')
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let resolveToolRoot = require('../../resolve-tool-root')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let version = require('../../../package.json').version
|
||||
|
||||
|
|
@ -19,7 +18,6 @@ let {
|
|||
})
|
||||
|
||||
let EXECUTABLE = 'node ../../lib/cli.js'
|
||||
let testStable = env.ENGINE === 'stable' ? test : test.skip
|
||||
|
||||
function dedent(input) {
|
||||
let lines = input.split('\n')
|
||||
|
|
@ -98,26 +96,36 @@ describe('Build command', () => {
|
|||
expect(withoutMinify.length).toBeGreaterThan(withMinify.length)
|
||||
})
|
||||
|
||||
testStable('--no-autoprefixer', async () => {
|
||||
// Deprecated
|
||||
test('--no-autoprefixer (should produce a warning)', async () => {
|
||||
await writeInputFile('index.html', html`<div class="select-none"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
@tailwind utilities;
|
||||
`
|
||||
)
|
||||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css`)
|
||||
let withAutoprefixer = await readOutputFile('main.css')
|
||||
let runningProcess = $(
|
||||
`${EXECUTABLE} --input ./src/index.css --output ./dist/main.css --no-autoprefixer`
|
||||
)
|
||||
let warning = runningProcess.onStderr((message) => message.includes('--no-autoprefixer'))
|
||||
await runningProcess
|
||||
expect(await warning).toMatchInlineSnapshot(`
|
||||
"[deprecation] The --no-autoprefixer flag is deprecated and has no effect.
|
||||
"
|
||||
`)
|
||||
let withoutAutoprefixer = await readOutputFile('main.css')
|
||||
|
||||
expect(withAutoprefixer).toIncludeCss(css`
|
||||
// This contains --webkit-user-select which may be strange, but it is expected because we are
|
||||
// not handling the `--no-autoprefixer` flag anymore at all.
|
||||
expect(withoutAutoprefixer).toMatchInlineSnapshot(`
|
||||
"/* ! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com */
|
||||
.select-none {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
`)
|
||||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css --no-autoprefixer`)
|
||||
let withoutAutoprefixer = await readOutputFile('main.css')
|
||||
|
||||
expect(withoutAutoprefixer).toIncludeCss(css`
|
||||
.select-none {
|
||||
user-select: none;
|
||||
}
|
||||
"
|
||||
`)
|
||||
})
|
||||
|
||||
|
|
@ -143,7 +151,7 @@ describe('Build command', () => {
|
|||
theme: {
|
||||
extend: {
|
||||
fontWeight: {
|
||||
bold: 'BOLD',
|
||||
bold: 'bold',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -164,7 +172,7 @@ describe('Build command', () => {
|
|||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold {
|
||||
font-weight: BOLD;
|
||||
font-weight: bold;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
|
@ -184,7 +192,7 @@ describe('Build command', () => {
|
|||
)
|
||||
})
|
||||
|
||||
testStable('--postcss (postcss.config.js)', async () => {
|
||||
test('--postcss (postcss.config.js)', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
|
|
@ -196,7 +204,7 @@ describe('Build command', () => {
|
|||
function before(root, result) {
|
||||
// Inject a custom component with @apply rules to prove that we run
|
||||
// this _before_ the actual tailwind plugin.
|
||||
let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }')
|
||||
let btn = postcss.parse('.btn { @apply flex px-2 py-1 }')
|
||||
root.append(btn.nodes)
|
||||
},
|
||||
function tailwindcss() {
|
||||
|
|
@ -217,45 +225,21 @@ describe('Build command', () => {
|
|||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css --postcss`)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
background-color: #ef4444;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
.btn-after {
|
||||
padding: 0.25rem 0.5rem;
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
testStable('--postcss (custom.postcss.config.js)', async () => {
|
||||
test('--postcss (custom.postcss.config.js)', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
|
|
@ -267,7 +251,7 @@ describe('Build command', () => {
|
|||
function before(root, result) {
|
||||
// Inject a custom component with @apply rules to prove that we run
|
||||
// this _before_ the actual tailwind plugin.
|
||||
let btn = postcss.parse('.btn { @apply bg-red-500 px-2 py-1 }')
|
||||
let btn = postcss.parse('.btn { @apply flex px-2 py-1 }')
|
||||
root.append(btn.nodes)
|
||||
},
|
||||
function tailwindcss() {
|
||||
|
|
@ -288,45 +272,21 @@ describe('Build command', () => {
|
|||
|
||||
await $(`${EXECUTABLE} --output ./dist/main.css --postcss ./custom.postcss.config.js`)
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.font-bold-after {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-after {
|
||||
background-color: #ef4444;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
.btn-after {
|
||||
padding: 0.25rem 0.5rem;
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
testStable('--postcss supports process options', async () => {
|
||||
test('--postcss supports process options', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
|
|
@ -360,7 +320,7 @@ describe('Build command', () => {
|
|||
expect(contents).toContain(`/*# sourceMappingURL`)
|
||||
})
|
||||
|
||||
testStable('--postcss supports process options with custom config', async () => {
|
||||
test('--postcss supports process options with custom config', async () => {
|
||||
await writeInputFile('index.html', html`<div class="font-bold"></div>`)
|
||||
|
||||
let customConfig = javascript`
|
||||
|
|
@ -472,7 +432,7 @@ describe('Build command', () => {
|
|||
css`
|
||||
@media (min-width: 768px) {
|
||||
.md\:something-cool {
|
||||
color: blue;
|
||||
color: #00f;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
@ -481,10 +441,18 @@ describe('Build command', () => {
|
|||
return runningProcess.stop()
|
||||
})
|
||||
|
||||
testStable('postcss-import is included when using a custom postcss configuration', async () => {
|
||||
test('postcss-import is included when using a custom postcss configuration', async () => {
|
||||
cleanupFile('src/test.css')
|
||||
|
||||
await writeInputFile('index.html', html`<div class="md:something-cool"></div>`)
|
||||
await writeInputFile(
|
||||
'imported.css',
|
||||
css`
|
||||
.foo {
|
||||
color: white;
|
||||
}
|
||||
`
|
||||
)
|
||||
await writeInputFile(
|
||||
'test.css',
|
||||
css`
|
||||
|
|
@ -501,7 +469,9 @@ describe('Build command', () => {
|
|||
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
@import './imported.css';
|
||||
.foo {
|
||||
color: #fff;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
|
@ -509,28 +479,8 @@ describe('Build command', () => {
|
|||
test('--help', async () => {
|
||||
let { combined } = await $(`${EXECUTABLE} --help`)
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
tailwindcss v${version}
|
||||
|
||||
Usage:
|
||||
tailwindcss build [options]
|
||||
|
||||
Options:
|
||||
-i, --input Input file
|
||||
-o, --output Output file
|
||||
-w, --watch Watch for changes and rebuild as needed
|
||||
-p, --poll Use polling instead of filesystem events when watching
|
||||
--content Content paths to use for removing unused classes
|
||||
-m, --minify Minify the output
|
||||
-c, --config Path to a custom config file
|
||||
-h, --help Display usage information
|
||||
`)
|
||||
)
|
||||
} else if (env.ENGINE === 'stable') {
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
tailwindcss v${version}
|
||||
|
||||
Usage:
|
||||
|
|
@ -545,11 +495,9 @@ describe('Build command', () => {
|
|||
--postcss Load custom PostCSS configuration
|
||||
-m, --minify Minify the output
|
||||
-c, --config Path to a custom config file
|
||||
--no-autoprefixer Disable autoprefixer
|
||||
-h, --help Display usage information
|
||||
`)
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -562,7 +510,6 @@ describe('Init command', () => {
|
|||
{ flags: ['--ts', '--full'], name: 'tailwind.config.ts' },
|
||||
{ flags: ['--esm', '--full'], name: 'tailwind.config.js' },
|
||||
])('works with all these flags: %j', async ({ flags, name }) => {
|
||||
cleanupFile(name)
|
||||
await removeFile(name)
|
||||
|
||||
let { combined } = await $(`${EXECUTABLE} init ${flags.join(' ')}`)
|
||||
|
|
@ -611,7 +558,7 @@ describe('Init command', () => {
|
|||
expect((await readOutputFile('../full.config.js')).split('\n').length).toBeGreaterThan(50)
|
||||
})
|
||||
|
||||
testStable('--postcss', async () => {
|
||||
test('--postcss', async () => {
|
||||
expect(await fileExists('postcss.config.js')).toBe(true)
|
||||
await removeFile('postcss.config.js')
|
||||
expect(await fileExists('postcss.config.js')).toBe(false)
|
||||
|
|
@ -631,24 +578,8 @@ describe('Init command', () => {
|
|||
test('--help', async () => {
|
||||
let { combined } = await $(`${EXECUTABLE} init --help`)
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
tailwindcss v${version}
|
||||
|
||||
Usage:
|
||||
tailwindcss init [options]
|
||||
|
||||
Options:
|
||||
--esm Initialize configuration file as ESM
|
||||
--ts Initialize configuration file as TypeScript
|
||||
-f, --full Include the default values for all options in the generated configuration file
|
||||
-h, --help Display usage information
|
||||
`)
|
||||
)
|
||||
} else if (env.ENGINE === 'stable') {
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
expect(dedent(combined)).toEqual(
|
||||
dedent(`
|
||||
tailwindcss v${version}
|
||||
|
||||
Usage:
|
||||
|
|
@ -661,12 +592,10 @@ describe('Init command', () => {
|
|||
-f, --full Include the default values for all options in the generated configuration file
|
||||
-h, --help Display usage information
|
||||
`)
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test('ESM config is created by default in an ESM project', async () => {
|
||||
cleanupFile('tailwind.config.js')
|
||||
await removeFile('tailwind.config.js')
|
||||
|
||||
let pkg = await readOutputFile('../package.json')
|
||||
|
|
@ -696,7 +625,6 @@ describe('Init command', () => {
|
|||
})
|
||||
|
||||
test('CJS config is created by default in a non-ESM project', async () => {
|
||||
cleanupFile('tailwind.config.js')
|
||||
await removeFile('tailwind.config.js')
|
||||
|
||||
let pkg = await readOutputFile('../package.json')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -21,6 +21,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"vite": "^4.3.2"
|
||||
"vite": "^5.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ require('isomorphic-fetch')
|
|||
|
||||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let { readOutputFile, appendToInputFile, writeInputFile, removeFile } = require('../../io')({
|
||||
output: 'dist',
|
||||
|
|
@ -48,7 +47,7 @@ describe('static build', () => {
|
|||
'index.html',
|
||||
html`
|
||||
<link rel="stylesheet" href="./index.css" />
|
||||
<div class="bg-primary"></div>
|
||||
<div class="z-primary"></div>
|
||||
`
|
||||
)
|
||||
await removeFile('tailwind.config.js')
|
||||
|
|
@ -59,8 +58,8 @@ describe('static build', () => {
|
|||
content: ['index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -75,26 +74,13 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production', NO_COLOR: '1' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
it('can use a tailwind.config.ts configuration file', async () => {
|
||||
|
|
@ -102,7 +88,7 @@ describe('static build', () => {
|
|||
'index.html',
|
||||
html`
|
||||
<link rel="stylesheet" href="./index.css" />
|
||||
<div class="bg-primary"></div>
|
||||
<div class="z-primary"></div>
|
||||
`
|
||||
)
|
||||
await removeFile('tailwind.config.js')
|
||||
|
|
@ -115,8 +101,8 @@ describe('static build', () => {
|
|||
content: ['index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -131,26 +117,13 @@ describe('static build', () => {
|
|||
env: { NODE_ENV: 'production', NO_COLOR: '1' },
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile(/index.\w+\.css$/)).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -191,41 +164,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStdout((message) => message.includes('page reload'))
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -262,41 +216,22 @@ describe('watcher', () => {
|
|||
`
|
||||
)
|
||||
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
await runningProcess.onStdout((message) => message.includes('page reload'))
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -428,49 +363,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
await runningProcess.onStdout((message) => message.includes('hmr update /index.css'))
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
background-color: #ef4444;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await fetchCSS()).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let {
|
||||
appendToInputFile,
|
||||
|
|
@ -28,7 +27,7 @@ describe('static build', () => {
|
|||
|
||||
it('can use a tailwind.config.js configuration file with ESM syntax', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -44,8 +43,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -58,31 +57,18 @@ describe('static build', () => {
|
|||
|
||||
await $('webpack --mode=production')
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
it('can use a tailwind.config.ts configuration file', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -100,8 +86,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -114,26 +100,13 @@ describe('static build', () => {
|
|||
|
||||
await $('webpack --mode=production')
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -169,41 +142,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange('main.css', async () => {
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -239,41 +193,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange('main.css', async () => {
|
||||
await appendToInputFile('glob/index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('glob/index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -394,46 +329,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
background-color: #ef4444;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
let $ = require('../../execute')
|
||||
let { css, html, javascript } = require('../../syntax')
|
||||
let { env } = require('../../../lib/lib/sharedState')
|
||||
|
||||
let {
|
||||
appendToInputFile,
|
||||
|
|
@ -28,7 +27,7 @@ describe('static build', () => {
|
|||
|
||||
it('can use a tailwind.config.js configuration file with ESM syntax', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -44,8 +43,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -58,31 +57,18 @@ describe('static build', () => {
|
|||
|
||||
await $('webpack --mode=production')
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
it('can use a tailwind.config.ts configuration file', async () => {
|
||||
await removeFile('tailwind.config.js')
|
||||
await writeInputFile('index.html', html`<div class="bg-primary"></div>`)
|
||||
await writeInputFile('index.html', html`<div class="z-primary"></div>`)
|
||||
await writeInputFile(
|
||||
'index.css',
|
||||
css`
|
||||
|
|
@ -100,8 +86,8 @@ describe('static build', () => {
|
|||
content: ['./src/index.html'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: 'black',
|
||||
zIndex: {
|
||||
primary: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -114,26 +100,13 @@ describe('static build', () => {
|
|||
|
||||
await $('webpack --mode=production')
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-primary {
|
||||
background-color: black;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-primary {
|
||||
z-index: 0;
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -169,41 +142,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange('main.css', async () => {
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -239,41 +193,22 @@ describe('watcher', () => {
|
|||
)
|
||||
|
||||
await waitForOutputFileChange('main.css', async () => {
|
||||
await appendToInputFile('index.html', html`<div class="bg-red-500"></div>`)
|
||||
await appendToInputFile('index.html', html`<div class="z-0"></div>`)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
.font-normal {
|
||||
font-weight: 400;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -394,46 +329,25 @@ describe('watcher', () => {
|
|||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply rounded bg-red-500 px-2 py-1;
|
||||
@apply rounded z-0 px-2 py-1;
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
border-radius: 0.25rem;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
background-color: #ef4444;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.btn {
|
||||
z-index: 0;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
@ -447,7 +361,7 @@ describe('watcher', () => {
|
|||
content: {
|
||||
files: ['./src/index.html'],
|
||||
},
|
||||
safelist: ['bg-red-500','bg-red-600'],
|
||||
safelist: ['z-0', 'z-10'],
|
||||
theme: {
|
||||
extend: {
|
||||
},
|
||||
|
|
@ -464,43 +378,21 @@ describe('watcher', () => {
|
|||
|
||||
await waitForOutputFileCreation('main.css')
|
||||
|
||||
if (env.ENGINE === 'stable') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||
}
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.z-0 {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.bg-red-600 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
||||
}
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
if (env.ENGINE === 'oxide') {
|
||||
expect(await readOutputFile('main.css')).toIncludeCss(
|
||||
css`
|
||||
.bg-red-500 {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
.bg-red-600 {
|
||||
background-color: #dc2626;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
return runningProcess.stop()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
const prettier = require('prettier')
|
||||
const { diff } = require('jest-diff')
|
||||
const lightningcss = require('lightningcss')
|
||||
const log = require('../src/util/log').default
|
||||
const { version } = require('../package.json')
|
||||
|
||||
function license() {
|
||||
return `/* ! tailwindcss v${version} | MIT License | https://tailwindcss.com */\n`
|
||||
}
|
||||
|
||||
let warn
|
||||
|
||||
|
|
@ -22,46 +26,7 @@ function formatPrettier(input) {
|
|||
}
|
||||
|
||||
function format(input) {
|
||||
try {
|
||||
return lightningcss
|
||||
.transform({
|
||||
filename: 'input.css',
|
||||
code: Buffer.from(input),
|
||||
minify: false,
|
||||
targets: { chrome: 106 << 16 },
|
||||
drafts: {
|
||||
nesting: true,
|
||||
customMedia: true,
|
||||
},
|
||||
})
|
||||
.code.toString('utf8')
|
||||
} catch (err) {
|
||||
try {
|
||||
// Lightning CSS is pretty strict, so it will fail for `@media screen(md) {}` for example,
|
||||
// in that case we can fallback to prettier since it doesn't really care. However if an
|
||||
// actual syntax error is made, then we still want to show the proper error.
|
||||
return formatPrettier(input.replace(/\n/g, ''))
|
||||
} catch {
|
||||
let lines = err.source.split('\n')
|
||||
let e = new Error(
|
||||
[
|
||||
'Error formatting using Lightning CSS:',
|
||||
'',
|
||||
...[
|
||||
'```css',
|
||||
...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line),
|
||||
' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(),
|
||||
...lines.slice(err.loc.line, err.loc.line + 2),
|
||||
'```',
|
||||
],
|
||||
].join('\n')
|
||||
)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(e, toMatchFormattedCss)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return formatPrettier(input).replace(/\n{2,}/g, '\n')
|
||||
}
|
||||
|
||||
function toMatchFormattedCss(received = '', argument = '') {
|
||||
|
|
@ -71,6 +36,11 @@ function toMatchFormattedCss(received = '', argument = '') {
|
|||
promise: this.promise,
|
||||
}
|
||||
|
||||
// Drop the license from the tests such that we can purely focus on the actual CSS being
|
||||
// generated.
|
||||
received = received.replace(license(), '')
|
||||
argument = argument.replace(license(), '')
|
||||
|
||||
let formattedReceived = format(received)
|
||||
let formattedArgument = format(argument)
|
||||
|
||||
|
|
@ -86,10 +56,7 @@ function toMatchFormattedCss(received = '', argument = '') {
|
|||
)
|
||||
}
|
||||
: () => {
|
||||
let actual = formatPrettier(formattedReceived).replace(/\n\n/g, '\n')
|
||||
let expected = formatPrettier(formattedArgument).replace(/\n\n/g, '\n')
|
||||
|
||||
let diffString = diff(expected, actual, {
|
||||
let diffString = diff(formattedArgument, formattedReceived, {
|
||||
expand: this.expand,
|
||||
})
|
||||
|
||||
|
|
@ -98,8 +65,8 @@ function toMatchFormattedCss(received = '', argument = '') {
|
|||
'\n\n' +
|
||||
(diffString && diffString.includes('- Expect')
|
||||
? `Difference:\n\n${diffString}`
|
||||
: `Expected: ${this.utils.printExpected(expected)}\n` +
|
||||
`Received: ${this.utils.printReceived(actual)}`)
|
||||
: `Expected: ${this.utils.printExpected(formattedArgument)}\n` +
|
||||
`Received: ${this.utils.printReceived(formattedReceived)}`)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -107,13 +74,10 @@ function toMatchFormattedCss(received = '', argument = '') {
|
|||
}
|
||||
|
||||
expect.extend({
|
||||
// Compare two CSS strings with all whitespace removed
|
||||
// This is probably naive but it's fast and works well enough.
|
||||
toMatchCss: toMatchFormattedCss,
|
||||
toMatchFormattedCss: toMatchFormattedCss,
|
||||
toIncludeCss(received, argument) {
|
||||
let options = {
|
||||
comment: 'stripped(received).includes(stripped(argument))',
|
||||
comment: 'formatCSS(received).includes(formatCSS(argument))',
|
||||
isNot: this.isNot,
|
||||
promise: this.promise,
|
||||
}
|
||||
|
|
|
|||
4
nesting/index.d.ts
vendored
Normal file
4
nesting/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import type { AcceptedPlugin, PluginCreator } from 'postcss'
|
||||
|
||||
declare const plugin: PluginCreator<AcceptedPlugin | string | void>
|
||||
export = plugin
|
||||
687
oxide/Cargo.lock
generated
687
oxide/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -105,7 +105,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||
let candidates = content_paths.par_bridge().flat_map(|path| {
|
||||
read_lines(path)
|
||||
.unwrap()
|
||||
.filter_map(Result::ok)
|
||||
.map_while(Result::ok)
|
||||
.par_bridge()
|
||||
.flat_map_iter(|line| {
|
||||
Extractor::unique(line.as_bytes(), Default::default())
|
||||
|
|
|
|||
|
|
@ -12,14 +12,17 @@ fxhash = "0.2.1"
|
|||
crossbeam = "0.8.2"
|
||||
tracing = { version = "0.1.37", features = [] }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
walkdir = "2.3.3"
|
||||
ignore = "0.4.20"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3", features = ['html_reports'] }
|
||||
tempfile = "3.5.0"
|
||||
|
||||
[[bench]]
|
||||
name = "parse_candidates"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "parse_candidate_strings_from_files"
|
||||
name = "parse_candidate_strings"
|
||||
harness = false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use std::path::PathBuf;
|
||||
use tailwindcss_core::{parse_candidate_strings_from_files, ChangedContent};
|
||||
use tailwindcss_core::{parse_candidate_strings, ChangedContent, Parsing, IO};
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
// current_dir will be set to ./crates/core
|
||||
|
|
@ -11,7 +11,6 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||
|
||||
let mut all_files: Vec<(u64, PathBuf)> = std::fs::read_dir(fixtures_path)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.map(|dir_entry| dir_entry.path())
|
||||
.filter(|path| path.is_file())
|
||||
|
|
@ -51,8 +50,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||
})
|
||||
.collect();
|
||||
|
||||
c.bench_function("parse_candidate_strings_from_files", |b| {
|
||||
b.iter(|| parse_candidate_strings_from_files(changed_content.clone()))
|
||||
c.bench_function("parse_candidate_strings", |b| {
|
||||
b.iter(|| {
|
||||
parse_candidate_strings(
|
||||
changed_content.clone(),
|
||||
Parsing::Parallel as u8 | IO::Parallel as u8,
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +31,18 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|||
c.bench_function("parse_candidate_strings (real world)", |b| {
|
||||
b.iter(|| parse(include_bytes!("./fixtures/template-499.html")))
|
||||
});
|
||||
|
||||
let mut group = c.benchmark_group("sample-size-example");
|
||||
group.sample_size(10);
|
||||
|
||||
group.bench_function("parse_candidate_strings (fast space skipping)", |b| {
|
||||
let count = 10_000;
|
||||
let crazy1 = format!("{}underline", " ".repeat(count));
|
||||
let crazy2 = crazy1.repeat(count);
|
||||
let crazy3 = crazy2.as_bytes();
|
||||
|
||||
b.iter(|| parse(black_box(crazy3)))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ impl Candidate {
|
|||
x == ':' && !in_arbitrary
|
||||
}
|
||||
})
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let utility = match parts.pop() {
|
||||
|
|
|
|||
159
oxide/crates/core/src/cursor.rs
Normal file
159
oxide/crates/core/src/cursor.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::{ascii::escape_default, fmt::Display};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cursor<'a> {
|
||||
// The input we're scanning
|
||||
pub input: &'a [u8],
|
||||
|
||||
// The location of the cursor in the input
|
||||
pub pos: usize,
|
||||
|
||||
/// Is the cursor at the start of the input
|
||||
pub at_start: bool,
|
||||
|
||||
/// Is the cursor at the end of the input
|
||||
pub at_end: bool,
|
||||
|
||||
/// The previously consumed character
|
||||
/// If `at_start` is true, this will be NUL
|
||||
pub prev: u8,
|
||||
|
||||
/// The current character
|
||||
pub curr: u8,
|
||||
|
||||
/// The upcoming character (if any)
|
||||
/// If `at_end` is true, this will be NUL
|
||||
pub next: u8,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
pub fn new(input: &'a [u8]) -> Self {
|
||||
let mut cursor = Self {
|
||||
input,
|
||||
pos: 0,
|
||||
at_start: true,
|
||||
at_end: false,
|
||||
prev: 0x00,
|
||||
curr: 0x00,
|
||||
next: 0x00,
|
||||
};
|
||||
cursor.move_to(0);
|
||||
cursor
|
||||
}
|
||||
|
||||
pub fn rewind_by(&mut self, amount: usize) {
|
||||
self.move_to(self.pos.saturating_sub(amount));
|
||||
}
|
||||
|
||||
pub fn advance_by(&mut self, amount: usize) {
|
||||
self.move_to(self.pos.saturating_add(amount));
|
||||
}
|
||||
|
||||
pub fn move_to(&mut self, pos: usize) {
|
||||
let len = self.input.len();
|
||||
let pos = pos.clamp(0, len);
|
||||
|
||||
self.pos = pos;
|
||||
self.at_start = pos == 0;
|
||||
self.at_end = pos + 1 >= len;
|
||||
|
||||
self.prev = if pos > 0 { self.input[pos - 1] } else { 0x00 };
|
||||
self.curr = if pos < len { self.input[pos] } else { 0x00 };
|
||||
self.next = if pos + 1 < len {
|
||||
self.input[pos + 1]
|
||||
} else {
|
||||
0x00
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Cursor<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let len = self.input.len().to_string();
|
||||
|
||||
let pos = format!("{: >len_count$}", self.pos, len_count = len.len());
|
||||
write!(f, "{}/{} ", pos, len)?;
|
||||
|
||||
if self.at_start {
|
||||
write!(f, "S ")?;
|
||||
} else if self.at_end {
|
||||
write!(f, "E ")?;
|
||||
} else {
|
||||
write!(f, "M ")?;
|
||||
}
|
||||
|
||||
fn to_str(c: u8) -> String {
|
||||
if c == 0x00 {
|
||||
"NUL".into()
|
||||
} else {
|
||||
format!("{:?}", escape_default(c).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"[{} {} {}]",
|
||||
to_str(self.prev),
|
||||
to_str(self.curr),
|
||||
to_str(self.next)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cursor() {
|
||||
let mut cursor = Cursor::new(b"hello world");
|
||||
assert_eq!(cursor.pos, 0);
|
||||
assert!(cursor.at_start);
|
||||
assert!(!cursor.at_end);
|
||||
assert_eq!(cursor.prev, 0x00);
|
||||
assert_eq!(cursor.curr, b'h');
|
||||
assert_eq!(cursor.next, b'e');
|
||||
|
||||
cursor.advance_by(1);
|
||||
assert_eq!(cursor.pos, 1);
|
||||
assert!(!cursor.at_start);
|
||||
assert!(!cursor.at_end);
|
||||
assert_eq!(cursor.prev, b'h');
|
||||
assert_eq!(cursor.curr, b'e');
|
||||
assert_eq!(cursor.next, b'l');
|
||||
|
||||
// Advancing too far should stop at the end
|
||||
cursor.advance_by(10);
|
||||
assert_eq!(cursor.pos, 11);
|
||||
assert!(!cursor.at_start);
|
||||
assert!(cursor.at_end);
|
||||
assert_eq!(cursor.prev, b'd');
|
||||
assert_eq!(cursor.curr, 0x00);
|
||||
assert_eq!(cursor.next, 0x00);
|
||||
|
||||
// Can't advance past the end
|
||||
cursor.advance_by(1);
|
||||
assert_eq!(cursor.pos, 11);
|
||||
assert!(!cursor.at_start);
|
||||
assert!(cursor.at_end);
|
||||
assert_eq!(cursor.prev, b'd');
|
||||
assert_eq!(cursor.curr, 0x00);
|
||||
assert_eq!(cursor.next, 0x00);
|
||||
|
||||
cursor.rewind_by(1);
|
||||
assert_eq!(cursor.pos, 10);
|
||||
assert!(!cursor.at_start);
|
||||
assert!(cursor.at_end);
|
||||
assert_eq!(cursor.prev, b'l');
|
||||
assert_eq!(cursor.curr, b'd');
|
||||
assert_eq!(cursor.next, 0x00);
|
||||
|
||||
cursor.rewind_by(10);
|
||||
assert_eq!(cursor.pos, 0);
|
||||
assert!(cursor.at_start);
|
||||
assert!(!cursor.at_end);
|
||||
assert_eq!(cursor.prev, 0x00);
|
||||
assert_eq!(cursor.curr, b'h');
|
||||
assert_eq!(cursor.next, b'e');
|
||||
}
|
||||
}
|
||||
89
oxide/crates/core/src/fast_skip.rs
Normal file
89
oxide/crates/core/src/fast_skip.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use crate::cursor::Cursor;
|
||||
|
||||
const STRIDE: usize = 16;
|
||||
type Mask = [bool; STRIDE];
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fast_skip(cursor: &Cursor) -> Option<usize> {
|
||||
// If we don't have enough bytes left to check then bail early
|
||||
if cursor.pos + STRIDE >= cursor.input.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !cursor.curr.is_ascii_whitespace() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut offset = 1;
|
||||
|
||||
// SAFETY: We've already checked (indirectly) that this index is valid
|
||||
let remaining = unsafe { cursor.input.get_unchecked(cursor.pos..) };
|
||||
|
||||
// NOTE: This loop uses primitives designed to be auto-vectorized
|
||||
// Do not change this loop without benchmarking the results
|
||||
// And checking the generated assembly using godbolt.org
|
||||
for (i, chunk) in remaining.chunks_exact(STRIDE).enumerate() {
|
||||
let value = load(chunk);
|
||||
let is_whitespace = is_ascii_whitespace(value);
|
||||
let is_all_whitespace = all_true(is_whitespace);
|
||||
|
||||
if is_all_whitespace {
|
||||
offset = (i + 1) * STRIDE;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some(cursor.pos + offset)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn load(input: &[u8]) -> [u8; STRIDE] {
|
||||
let mut value = [0u8; STRIDE];
|
||||
value.copy_from_slice(input);
|
||||
value
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn eq(input: [u8; STRIDE], val: u8) -> Mask {
|
||||
let mut res = [false; STRIDE];
|
||||
for n in 0..STRIDE {
|
||||
res[n] = input[n] == val
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn or(a: [bool; STRIDE], b: [bool; STRIDE]) -> [bool; STRIDE] {
|
||||
let mut res = [false; STRIDE];
|
||||
for n in 0..STRIDE {
|
||||
res[n] = a[n] | b[n];
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn all_true(a: [bool; STRIDE]) -> bool {
|
||||
let mut res = true;
|
||||
for item in a.iter().take(STRIDE) {
|
||||
res &= item;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_ascii_whitespace(value: [u8; STRIDE]) -> [bool; STRIDE] {
|
||||
let whitespace_1 = eq(value, b'\t');
|
||||
let whitespace_2 = eq(value, b'\n');
|
||||
let whitespace_3 = eq(value, b'\x0C');
|
||||
let whitespace_4 = eq(value, b'\r');
|
||||
let whitespace_5 = eq(value, b' ');
|
||||
|
||||
or(
|
||||
or(
|
||||
or(or(whitespace_1, whitespace_2), whitespace_3),
|
||||
whitespace_4,
|
||||
),
|
||||
whitespace_5,
|
||||
)
|
||||
}
|
||||
259
oxide/crates/core/src/fixtures/binary-extensions.txt
Normal file
259
oxide/crates/core/src/fixtures/binary-extensions.txt
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
3dm
|
||||
3ds
|
||||
3g2
|
||||
3gp
|
||||
7z
|
||||
DS_Store
|
||||
a
|
||||
aac
|
||||
adp
|
||||
ai
|
||||
aif
|
||||
aiff
|
||||
alz
|
||||
ape
|
||||
apk
|
||||
appimage
|
||||
ar
|
||||
arj
|
||||
asf
|
||||
au
|
||||
avi
|
||||
bak
|
||||
baml
|
||||
bh
|
||||
bin
|
||||
bk
|
||||
bmp
|
||||
btif
|
||||
bz2
|
||||
bzip2
|
||||
cab
|
||||
caf
|
||||
cgm
|
||||
class
|
||||
cmx
|
||||
cpio
|
||||
cr2
|
||||
cur
|
||||
dat
|
||||
dcm
|
||||
deb
|
||||
dex
|
||||
djvu
|
||||
dll
|
||||
dmg
|
||||
dng
|
||||
doc
|
||||
docm
|
||||
docx
|
||||
dot
|
||||
dotm
|
||||
dra
|
||||
dsk
|
||||
dts
|
||||
dtshd
|
||||
dvb
|
||||
dwg
|
||||
dxf
|
||||
ecelp4800
|
||||
ecelp7470
|
||||
ecelp9600
|
||||
egg
|
||||
eol
|
||||
eot
|
||||
epub
|
||||
exe
|
||||
f4v
|
||||
fbs
|
||||
fh
|
||||
fla
|
||||
flac
|
||||
flatpak
|
||||
fli
|
||||
flv
|
||||
fpx
|
||||
fst
|
||||
fvt
|
||||
g3
|
||||
gh
|
||||
gif
|
||||
graffle
|
||||
gz
|
||||
gzip
|
||||
h261
|
||||
h263
|
||||
h264
|
||||
icns
|
||||
ico
|
||||
ief
|
||||
img
|
||||
ipa
|
||||
iso
|
||||
jar
|
||||
jpeg
|
||||
jpg
|
||||
jpgv
|
||||
jpm
|
||||
jxr
|
||||
key
|
||||
ktx
|
||||
lha
|
||||
lib
|
||||
lockb
|
||||
lvp
|
||||
lz
|
||||
lzh
|
||||
lzma
|
||||
lzo
|
||||
m3u
|
||||
m4a
|
||||
m4v
|
||||
mar
|
||||
mdi
|
||||
mht
|
||||
mid
|
||||
midi
|
||||
mj2
|
||||
mka
|
||||
mkv
|
||||
mmr
|
||||
mng
|
||||
mobi
|
||||
mov
|
||||
movie
|
||||
mp3
|
||||
mp4
|
||||
mp4a
|
||||
mpeg
|
||||
mpg
|
||||
mpga
|
||||
mxu
|
||||
nef
|
||||
npx
|
||||
numbers
|
||||
nupkg
|
||||
o
|
||||
odp
|
||||
ods
|
||||
odt
|
||||
oga
|
||||
ogg
|
||||
ogv
|
||||
otf
|
||||
ott
|
||||
pages
|
||||
pbm
|
||||
pcx
|
||||
pdb
|
||||
pdf
|
||||
pea
|
||||
pgm
|
||||
pic
|
||||
png
|
||||
pnm
|
||||
pot
|
||||
potm
|
||||
potx
|
||||
ppa
|
||||
ppam
|
||||
ppm
|
||||
pps
|
||||
ppsm
|
||||
ppsx
|
||||
ppt
|
||||
pptm
|
||||
pptx
|
||||
psd
|
||||
pya
|
||||
pyc
|
||||
pyo
|
||||
pyv
|
||||
qt
|
||||
rar
|
||||
ras
|
||||
raw
|
||||
resources
|
||||
rgb
|
||||
rip
|
||||
rlc
|
||||
rmf
|
||||
rmvb
|
||||
rpm
|
||||
rtf
|
||||
rz
|
||||
s3m
|
||||
s7z
|
||||
scpt
|
||||
sgi
|
||||
shar
|
||||
sil
|
||||
sketch
|
||||
slk
|
||||
smv
|
||||
snap
|
||||
snk
|
||||
so
|
||||
stl
|
||||
sub
|
||||
suo
|
||||
swf
|
||||
tar
|
||||
tbz
|
||||
tbz2
|
||||
tga
|
||||
tgz
|
||||
thmx
|
||||
tif
|
||||
tiff
|
||||
tlz
|
||||
ttc
|
||||
ttf
|
||||
txz
|
||||
udf
|
||||
uvh
|
||||
uvi
|
||||
uvm
|
||||
uvp
|
||||
uvs
|
||||
uvu
|
||||
viv
|
||||
vob
|
||||
war
|
||||
wav
|
||||
wax
|
||||
wbmp
|
||||
wdp
|
||||
weba
|
||||
webm
|
||||
webp
|
||||
whl
|
||||
wim
|
||||
wm
|
||||
wma
|
||||
wmv
|
||||
wmx
|
||||
woff
|
||||
woff2
|
||||
wrm
|
||||
wvx
|
||||
xbm
|
||||
xif
|
||||
xla
|
||||
xlam
|
||||
xls
|
||||
xlsb
|
||||
xlsm
|
||||
xlsx
|
||||
xlt
|
||||
xltm
|
||||
xltx
|
||||
xm
|
||||
xmind
|
||||
xpi
|
||||
xpm
|
||||
xwd
|
||||
xz
|
||||
z
|
||||
zip
|
||||
zipx
|
||||
6
oxide/crates/core/src/fixtures/ignored-extensions.txt
Normal file
6
oxide/crates/core/src/fixtures/ignored-extensions.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
css
|
||||
less
|
||||
lock
|
||||
sass
|
||||
scss
|
||||
styl
|
||||
3
oxide/crates/core/src/fixtures/ignored-files.txt
Normal file
3
oxide/crates/core/src/fixtures/ignored-files.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
bun.lockb
|
||||
57
oxide/crates/core/src/fixtures/template-extensions.txt
Normal file
57
oxide/crates/core/src/fixtures/template-extensions.txt
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# HTML
|
||||
html
|
||||
pug
|
||||
|
||||
# JS
|
||||
astro
|
||||
cjs
|
||||
cts
|
||||
jade
|
||||
js
|
||||
jsx
|
||||
mjs
|
||||
mts
|
||||
svelte
|
||||
ts
|
||||
tsx
|
||||
vue
|
||||
|
||||
# Markdown
|
||||
md
|
||||
mdx
|
||||
|
||||
# ASP
|
||||
aspx
|
||||
razor
|
||||
|
||||
# Handlebars
|
||||
handlebars
|
||||
hbs
|
||||
mustache
|
||||
|
||||
# PHP
|
||||
php
|
||||
twig
|
||||
|
||||
# Ruby
|
||||
erb
|
||||
haml
|
||||
liquid
|
||||
rb
|
||||
rhtml
|
||||
slim
|
||||
|
||||
# Elixir / Phoenix
|
||||
eex
|
||||
heex
|
||||
|
||||
# Nunjucks
|
||||
njk
|
||||
nunjucks
|
||||
|
||||
# Python
|
||||
py
|
||||
tpl
|
||||
|
||||
# Rust
|
||||
rs
|
||||
|
|
@ -12,7 +12,6 @@ pub fn fast_glob(
|
|||
.follow_links(true)
|
||||
.build()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.map(|file| file.path().to_path_buf())
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
use crate::parser::Extractor;
|
||||
use fxhash::FxHashSet;
|
||||
use ignore::WalkBuilder;
|
||||
use rayon::prelude::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tracing::event;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub mod candidate;
|
||||
pub mod cursor;
|
||||
pub mod fast_skip;
|
||||
pub mod glob;
|
||||
pub mod location;
|
||||
pub mod modifier;
|
||||
|
|
@ -11,14 +18,7 @@ pub mod parser;
|
|||
pub mod utility;
|
||||
pub mod variant;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChangedContent {
|
||||
pub file: Option<PathBuf>,
|
||||
pub content: Option<String>,
|
||||
pub extension: String,
|
||||
}
|
||||
|
||||
pub fn parse_candidate_strings_from_files(changed_content: Vec<ChangedContent>) -> Vec<String> {
|
||||
fn init_tracing() {
|
||||
if matches!(std::env::var("DEBUG"), Ok(value) if value.eq("*") || value.eq("1") || value.eq("true") || value.contains("tailwind"))
|
||||
{
|
||||
tracing_subscriber::fmt()
|
||||
|
|
@ -27,8 +27,325 @@ pub fn parse_candidate_strings_from_files(changed_content: Vec<ChangedContent>)
|
|||
.compact()
|
||||
.init();
|
||||
}
|
||||
}
|
||||
|
||||
parse_all_blobs(read_all_files(changed_content))
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChangedContent {
|
||||
pub file: Option<PathBuf>,
|
||||
pub content: Option<String>,
|
||||
pub extension: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContentPathInfo {
|
||||
pub base: String,
|
||||
}
|
||||
|
||||
pub fn resolve_content_paths(args: ContentPathInfo) -> Vec<String> {
|
||||
let root = Path::new(&args.base);
|
||||
|
||||
let allowed_paths = FxHashSet::from_iter(
|
||||
WalkBuilder::new(root)
|
||||
.hidden(false)
|
||||
.filter_entry(|entry| match entry.file_type() {
|
||||
Some(file_type) if file_type.is_dir() => entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| s != ".git")
|
||||
.unwrap_or(false),
|
||||
_ => true,
|
||||
})
|
||||
.build()
|
||||
.filter_map(Result::ok)
|
||||
.map(|x| x.into_path()),
|
||||
);
|
||||
|
||||
// A list of directory names where we can't use globs, but we should track each file
|
||||
// individually instead. This is because these directories are often used for both source and
|
||||
// destination files.
|
||||
let mut forced_static_directories = vec![root.join("public")];
|
||||
|
||||
// A list of known extensions + a list of extensions we found in the project.
|
||||
let mut found_extensions = FxHashSet::from_iter(
|
||||
include_str!("fixtures/template-extensions.txt")
|
||||
.trim()
|
||||
.lines()
|
||||
.filter(|x| !x.starts_with('#')) // Drop commented lines
|
||||
.filter(|x| !x.is_empty()) // Drop empty lines
|
||||
.map(|x| x.to_string()),
|
||||
);
|
||||
|
||||
// A list of static files that we should track directly.
|
||||
let mut static_files = vec![];
|
||||
|
||||
// All root directories.
|
||||
let mut root_directories = FxHashSet::from_iter(vec![root.to_path_buf()]);
|
||||
|
||||
// All directories where we can safely use deeply nested globs to watch all files.
|
||||
// In other comments we refer to these as "deep glob directories" or similar.
|
||||
//
|
||||
// E.g.: ./src/**/*.{html,js}
|
||||
let mut deep_globable_directories: FxHashSet<PathBuf> = FxHashSet::default();
|
||||
|
||||
// All directories where we can only use shallow globs to watch all direct files but not
|
||||
// folders.
|
||||
// In other comments we refer to these as "shallow glob directories" or similar.
|
||||
//
|
||||
// E.g.: ./src/*/*.{html,js}
|
||||
let mut shallow_globable_directories: FxHashSet<PathBuf> = FxHashSet::default();
|
||||
|
||||
// Collect all valid paths from the root. This will already filter out ignored files, unknown
|
||||
// extensions and binary files.
|
||||
let mut it = WalkDir::new(root)
|
||||
// Sorting to make sure that we always see the directories before the files. Also sorting
|
||||
// alphabetically by default.
|
||||
.sort_by(
|
||||
|a, z| match (a.file_type().is_dir(), z.file_type().is_dir()) {
|
||||
(true, false) => Ordering::Less,
|
||||
(false, true) => Ordering::Greater,
|
||||
_ => a.file_name().cmp(z.file_name()),
|
||||
},
|
||||
)
|
||||
.into_iter();
|
||||
|
||||
loop {
|
||||
// We are only interested in valid entries
|
||||
let entry = match it.next() {
|
||||
Some(Ok(entry)) => entry,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
// Ignore known directories that we don't want to traverse into.
|
||||
if entry.file_type().is_dir() && entry.file_name() == ".git" {
|
||||
it.skip_current_dir();
|
||||
continue;
|
||||
}
|
||||
|
||||
if entry.file_type().is_dir() {
|
||||
// If we are in a directory where we know that we can't use any globs, then we have to
|
||||
// track each file individually.
|
||||
if forced_static_directories.contains(&entry.path().to_path_buf()) {
|
||||
forced_static_directories.push(entry.path().to_path_buf());
|
||||
root_directories.insert(entry.path().to_path_buf());
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are in a directory where the parent is a forced static directory, then this
|
||||
// will become a forced static directory as well.
|
||||
if forced_static_directories.contains(&entry.path().parent().unwrap().to_path_buf()) {
|
||||
forced_static_directories.push(entry.path().to_path_buf());
|
||||
root_directories.insert(entry.path().to_path_buf());
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are in a directory, and the directory is git ignored, then we don't have to
|
||||
// descent into the directory. However, we have to make sure that we mark the _parent_
|
||||
// directory as a shallow glob directory because using deep globs from any of the
|
||||
// parent directories will include this ignored directory which should not be the case.
|
||||
//
|
||||
// Another important part is that if one of the ignored directories is a deep glob
|
||||
// directory, then all of its parents (until the root) should be marked as shallow glob
|
||||
// directories as well.
|
||||
if !allowed_paths.contains(&entry.path().to_path_buf()) {
|
||||
let mut parent = entry.path().parent();
|
||||
while let Some(parent_path) = parent {
|
||||
// If the parent is already marked as a valid deep glob directory, then we have
|
||||
// to mark it as a shallow glob directory instead, because we won't be able to
|
||||
// use deep globs for this directory anymore.
|
||||
if deep_globable_directories.contains(parent_path) {
|
||||
deep_globable_directories.remove(parent_path);
|
||||
shallow_globable_directories.insert(parent_path.to_path_buf());
|
||||
}
|
||||
|
||||
// If we reached the root, then we can stop.
|
||||
if parent_path == root {
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark the parent directory as a shallow glob directory and continue with its
|
||||
// parent.
|
||||
shallow_globable_directories.insert(parent_path.to_path_buf());
|
||||
parent = parent_path.parent();
|
||||
}
|
||||
|
||||
it.skip_current_dir();
|
||||
continue;
|
||||
}
|
||||
// If we are in a directory that is not git ignored, then we can mark this directory as
|
||||
// a valid deep glob directory. This is only necessary if any of its parents aren't
|
||||
// marked as deep glob directories already.
|
||||
else {
|
||||
let mut found_deep_glob_parent = false;
|
||||
let mut parent = entry.path().parent();
|
||||
while let Some(parent_path) = parent {
|
||||
// If we reached the root, then we can stop.
|
||||
if parent_path == root {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the parent is already marked as a deep glob directory, then we can stop
|
||||
// because this glob will match the current directory already.
|
||||
if deep_globable_directories.contains(parent_path) {
|
||||
found_deep_glob_parent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent_path.parent();
|
||||
}
|
||||
|
||||
// If we didn't find a deep glob directory parent, then we can mark this directory
|
||||
// as a deep glob directory (unless it is the root).
|
||||
if !found_deep_glob_parent && entry.path() != root {
|
||||
deep_globable_directories.insert(entry.path().to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle allowed content paths
|
||||
if is_allowed_content_path(entry.path())
|
||||
&& allowed_paths.contains(&entry.path().to_path_buf())
|
||||
{
|
||||
let path = entry.path();
|
||||
|
||||
// Collect the extension for future use when building globs.
|
||||
if let Some(extension) = path.extension().and_then(|x| x.to_str()) {
|
||||
found_extensions.insert(extension.to_string());
|
||||
}
|
||||
|
||||
// If the current path is a file inside any of the root directories, then we have to
|
||||
// track it directly as a static file.
|
||||
if let Some(parent_path) = path.parent() {
|
||||
if root_directories.contains(parent_path) {
|
||||
static_files.push(format!("{}", path.display()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the globs for all globable directories.
|
||||
let shallow_globs = shallow_globable_directories
|
||||
.iter()
|
||||
.map(|path| format!("{}", path.display()));
|
||||
let deep_globs = deep_globable_directories
|
||||
.iter()
|
||||
.map(|path| format!("{}/**", path.display()));
|
||||
let globs = shallow_globs
|
||||
.chain(deep_globs)
|
||||
.flat_map(|path| match found_extensions.len() {
|
||||
0 => None, // This should never happen
|
||||
1 => Some(format!(
|
||||
"{}/*.{}",
|
||||
path,
|
||||
found_extensions.iter().next().unwrap()
|
||||
)),
|
||||
_ => Some(format!(
|
||||
"{}/*.{{{}}}",
|
||||
path,
|
||||
found_extensions
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(","),
|
||||
)),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
static_files.extend(globs);
|
||||
static_files
|
||||
}
|
||||
|
||||
pub fn is_git_ignored_content_path(base: &Path, path: &Path) -> bool {
|
||||
!WalkBuilder::new(base)
|
||||
.hidden(false)
|
||||
.filter_entry(|entry| match entry.file_type() {
|
||||
Some(file_type) if file_type.is_dir() => entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| s != ".git")
|
||||
.unwrap_or(false),
|
||||
_ => true,
|
||||
})
|
||||
.build()
|
||||
.filter_map(Result::ok)
|
||||
.any(|e| e.path() == path)
|
||||
}
|
||||
|
||||
pub fn is_allowed_content_path(path: &Path) -> bool {
|
||||
let binary_extensions = include_str!("fixtures/binary-extensions.txt")
|
||||
.trim()
|
||||
.lines()
|
||||
.collect::<Vec<_>>();
|
||||
let ignored_extensions = include_str!("fixtures/ignored-extensions.txt")
|
||||
.trim()
|
||||
.lines()
|
||||
.collect::<Vec<_>>();
|
||||
let ignored_files = include_str!("fixtures/ignored-files.txt")
|
||||
.trim()
|
||||
.lines()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let path = PathBuf::from(path);
|
||||
|
||||
// Skip known ignored files
|
||||
if path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.map(|s| ignored_files.contains(&s))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip known ignored extensions
|
||||
return path
|
||||
.extension()
|
||||
.map(|s| s.to_str().unwrap_or_default())
|
||||
.map(|ext| !ignored_extensions.contains(&ext) && !binary_extensions.contains(&ext))
|
||||
.unwrap_or(false);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IO {
|
||||
Sequential = 0b0001,
|
||||
Parallel = 0b0010,
|
||||
}
|
||||
|
||||
impl From<u8> for IO {
|
||||
fn from(item: u8) -> Self {
|
||||
match item & 0b0011 {
|
||||
0b0001 => IO::Sequential,
|
||||
0b0010 => IO::Parallel,
|
||||
_ => unimplemented!("Unknown 'IO' strategy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Parsing {
|
||||
Sequential = 0b0100,
|
||||
Parallel = 0b1000,
|
||||
}
|
||||
|
||||
impl From<u8> for Parsing {
|
||||
fn from(item: u8) -> Self {
|
||||
match item & 0b1100 {
|
||||
0b0100 => Parsing::Sequential,
|
||||
0b1000 => Parsing::Parallel,
|
||||
_ => unimplemented!("Unknown 'Parsing' strategy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_candidate_strings(input: Vec<ChangedContent>, options: u8) -> Vec<String> {
|
||||
init_tracing();
|
||||
|
||||
match (IO::from(options), Parsing::from(options)) {
|
||||
(IO::Sequential, Parsing::Sequential) => parse_all_blobs_sync(read_all_files_sync(input)),
|
||||
(IO::Sequential, Parsing::Parallel) => parse_all_blobs(read_all_files_sync(input)),
|
||||
(IO::Parallel, Parsing::Sequential) => parse_all_blobs_sync(read_all_files(input)),
|
||||
(IO::Parallel, Parsing::Parallel) => parse_all_blobs(read_all_files(input)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(changed_content))]
|
||||
|
|
@ -41,6 +358,30 @@ fn read_all_files(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
|
|||
|
||||
changed_content
|
||||
.into_par_iter()
|
||||
.map(|c| match (c.file, c.content) {
|
||||
(Some(file), None) => match std::fs::read(file) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
event!(tracing::Level::ERROR, "Failed to read file: {:?}", e);
|
||||
Default::default()
|
||||
}
|
||||
},
|
||||
(None, Some(content)) => content.into_bytes(),
|
||||
_ => Default::default(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(changed_content))]
|
||||
fn read_all_files_sync(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
|
||||
event!(
|
||||
tracing::Level::INFO,
|
||||
"Reading {:?} file(s)",
|
||||
changed_content.len()
|
||||
);
|
||||
|
||||
changed_content
|
||||
.into_iter()
|
||||
.map(|c| match (c.file, c.content) {
|
||||
(Some(file), None) => std::fs::read(file).unwrap(),
|
||||
(None, Some(content)) => content.into_bytes(),
|
||||
|
|
@ -72,3 +413,27 @@ fn parse_all_blobs(blobs: Vec<Vec<u8>>) -> Vec<String> {
|
|||
result.sort();
|
||||
result
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(blobs))]
|
||||
fn parse_all_blobs_sync(blobs: Vec<Vec<u8>>) -> Vec<String> {
|
||||
let input: Vec<_> = blobs.iter().map(|blob| &blob[..]).collect();
|
||||
let input = &input[..];
|
||||
|
||||
let mut result: Vec<String> = input
|
||||
.iter()
|
||||
.map(|input| Extractor::unique(input, Default::default()))
|
||||
.fold(FxHashSet::default(), |mut a, b| {
|
||||
a.extend(b);
|
||||
a
|
||||
})
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
// SAFETY: When we parsed the candidates, we already guaranteed that the byte slices
|
||||
// are valid, therefore we don't have to re-check here when we want to convert it back
|
||||
// to a string.
|
||||
unsafe { String::from_utf8_unchecked(s.to_vec()) }
|
||||
})
|
||||
.collect();
|
||||
result.sort();
|
||||
result
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
248
oxide/crates/core/tests/auto_content.rs
Normal file
248
oxide/crates/core/tests/auto_content.rs
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
#[cfg(test)]
|
||||
mod auto_content {
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
use tailwindcss_core::*;
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn test(paths_with_content: &[(&str, Option<&str>)]) -> Vec<String> {
|
||||
// Create a temporary working directory
|
||||
let dir = tempdir().unwrap().into_path();
|
||||
|
||||
// Initialize this directory as a git repository
|
||||
let _ = Command::new("git").arg("init").current_dir(&dir).output();
|
||||
|
||||
// Create the necessary files
|
||||
for (path, contents) in paths_with_content {
|
||||
let path = dir.join(path);
|
||||
let parent = path.parent().unwrap();
|
||||
if !parent.exists() {
|
||||
fs::create_dir_all(parent).unwrap();
|
||||
}
|
||||
|
||||
match contents {
|
||||
Some(contents) => fs::write(path, contents).unwrap(),
|
||||
None => fs::write(path, "").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
let base = format!("{}", dir.display());
|
||||
|
||||
// Resolve all content paths for the (temporary) current working directory
|
||||
let mut paths: Vec<_> = resolve_content_paths(ContentPathInfo { base: base.clone() })
|
||||
.into_iter()
|
||||
.map(|x| x.replace(&format!("{}/", &base), ""))
|
||||
.collect();
|
||||
|
||||
// Sort the output for easier comparison (depending on internal datastructure the order
|
||||
// _could_ be random)
|
||||
paths.sort();
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_work_with_a_set_of_root_files() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("a.html", None),
|
||||
("b.html", None),
|
||||
("c.html", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["a.html", "b.html", "c.html", "index.html"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_work_with_a_set_of_root_files_and_ignore_ignored_files() {
|
||||
let globs = test(&[
|
||||
(".gitignore", Some("b.html")),
|
||||
("index.html", None),
|
||||
("a.html", None),
|
||||
("b.html", None),
|
||||
("c.html", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["a.html", "c.html", "index.html"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_list_all_files_in_the_public_folder_explicitly() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("public/a.html", None),
|
||||
("public/b.html", None),
|
||||
("public/c.html", None),
|
||||
]);
|
||||
assert_eq!(
|
||||
globs,
|
||||
vec![
|
||||
"index.html",
|
||||
"public/a.html",
|
||||
"public/b.html",
|
||||
"public/c.html",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_list_nested_folders_explicitly_in_the_public_folder() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("public/a.html", None),
|
||||
("public/b.html", None),
|
||||
("public/c.html", None),
|
||||
("public/nested/a.html", None),
|
||||
("public/nested/b.html", None),
|
||||
("public/nested/c.html", None),
|
||||
("public/nested/again/a.html", None),
|
||||
("public/very/deeply/nested/a.html", None),
|
||||
]);
|
||||
assert_eq!(
|
||||
globs,
|
||||
vec![
|
||||
"index.html",
|
||||
"public/a.html",
|
||||
"public/b.html",
|
||||
"public/c.html",
|
||||
"public/nested/a.html",
|
||||
"public/nested/again/a.html",
|
||||
"public/nested/b.html",
|
||||
"public/nested/c.html",
|
||||
"public/very/deeply/nested/a.html",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_list_all_files_in_the_public_folder_explicitly_except_ignored_files() {
|
||||
let globs = test(&[
|
||||
(".gitignore", Some("public/b.html\na.html")),
|
||||
("index.html", None),
|
||||
("public/a.html", None),
|
||||
("public/b.html", None),
|
||||
("public/c.html", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["index.html", "public/c.html",]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_use_a_glob_for_top_level_folders() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("src/a.html", None),
|
||||
("src/b.html", None),
|
||||
("src/c.html", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["index.html", "src/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_ignore_binary_files() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("a.mp4", None),
|
||||
("b.png", None),
|
||||
("c.lock", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["index.html"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_ignore_known_extensions() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("a.css", None),
|
||||
("b.sass", None),
|
||||
("c.less", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["index.html"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_ignore_known_files() {
|
||||
let globs = test(&[
|
||||
("index.html", None),
|
||||
("package-lock.json", None),
|
||||
("yarn.lock", None),
|
||||
]);
|
||||
assert_eq!(globs, vec!["index.html"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_ignore_and_expand_nested_ignored_folders() {
|
||||
let globs = test(&[
|
||||
// Explicitly listed root files
|
||||
("foo.html", None),
|
||||
("bar.html", None),
|
||||
("baz.html", None),
|
||||
// Nested folder A, using glob
|
||||
("nested-a/foo.html", None),
|
||||
("nested-a/bar.html", None),
|
||||
("nested-a/baz.html", None),
|
||||
// Nested folder B, with deeply nested files, using glob
|
||||
("nested-b/deeply-nested/foo.html", None),
|
||||
("nested-b/deeply-nested/bar.html", None),
|
||||
("nested-b/deeply-nested/baz.html", None),
|
||||
// Nested folder C, with ignored sub-folder
|
||||
("nested-c/foo.html", None),
|
||||
("nested-c/bar.html", None),
|
||||
("nested-c/baz.html", None),
|
||||
// Ignored folder
|
||||
("nested-c/.gitignore", Some("ignored-folder/")),
|
||||
("nested-c/ignored-folder/foo.html", None),
|
||||
("nested-c/ignored-folder/bar.html", None),
|
||||
("nested-c/ignored-folder/baz.html", None),
|
||||
// Deeply nested, without issues
|
||||
("nested-c/sibling-folder/foo.html", None),
|
||||
("nested-c/sibling-folder/bar.html", None),
|
||||
("nested-c/sibling-folder/baz.html", None),
|
||||
// Nested folder D, with deeply nested ignored folder
|
||||
("nested-d/foo.html", None),
|
||||
("nested-d/bar.html", None),
|
||||
("nested-d/baz.html", None),
|
||||
("nested-d/.gitignore", Some("deep/")),
|
||||
("nested-d/very/deeply/nested/deep/foo.html", None),
|
||||
("nested-d/very/deeply/nested/deep/bar.html", None),
|
||||
("nested-d/very/deeply/nested/deep/baz.html", None),
|
||||
("nested-d/very/deeply/nested/foo.html", None),
|
||||
("nested-d/very/deeply/nested/bar.html", None),
|
||||
("nested-d/very/deeply/nested/baz.html", None),
|
||||
("nested-d/very/deeply/nested/directory/foo.html", None),
|
||||
("nested-d/very/deeply/nested/directory/bar.html", None),
|
||||
("nested-d/very/deeply/nested/directory/baz.html", None),
|
||||
("nested-d/very/deeply/nested/directory/again/foo.html", None),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
globs,
|
||||
vec![
|
||||
// Listed explicitly because they are in the root
|
||||
"bar.html",
|
||||
"baz.html",
|
||||
"foo.html",
|
||||
|
||||
// Listed using a deep glob, because nothing inside is ignored
|
||||
"nested-a/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
|
||||
// Listed using a deep glob, because nothing inside is ignored (but contains deeply
|
||||
// nested folders)
|
||||
"nested-b/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
|
||||
// Listed using a shallow glob, because `nested-c` contains ignored folders, deeply
|
||||
// nested globs can't be used anymore
|
||||
"nested-c/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
|
||||
// Deeply nested folder can use a deep glob again
|
||||
"nested-c/sibling-folder/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
|
||||
// The deeply nested folder can use a deep glob, however all of its parents can
|
||||
// only contain a shallow glob because of the ignored folder
|
||||
"nested-d/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
"nested-d/very/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
"nested-d/very/deeply/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
"nested-d/very/deeply/nested/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
"nested-d/very/deeply/nested/directory/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}",
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||
napi = { version = "2.10.0", default-features = false, features = ["napi4"] }
|
||||
napi-derive = "2.11.1"
|
||||
napi = { version = "2.13.1", default-features = false, features = ["napi4"] }
|
||||
napi-derive = "2.13.0"
|
||||
tailwindcss-core = { path = "../core" }
|
||||
rayon = "1.5.3"
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.15.2"
|
||||
"@napi-rs/cli": "^2.16.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use napi::bindgen_prelude::{FromNapiValue, ToNapiValue};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[macro_use]
|
||||
|
|
@ -11,16 +12,42 @@ pub struct ChangedContent {
|
|||
pub extension: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn parse_candidate_strings_from_files(changed_content: Vec<ChangedContent>) -> Vec<String> {
|
||||
tailwindcss_core::parse_candidate_strings_from_files(
|
||||
changed_content
|
||||
.into_iter()
|
||||
.map(|changed_content| tailwindcss_core::ChangedContent {
|
||||
file: changed_content.file.map(PathBuf::from),
|
||||
content: changed_content.content,
|
||||
extension: changed_content.extension,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
impl From<ChangedContent> for tailwindcss_core::ChangedContent {
|
||||
fn from(changed_content: ChangedContent) -> Self {
|
||||
tailwindcss_core::ChangedContent {
|
||||
file: changed_content.file.map(PathBuf::from),
|
||||
content: changed_content.content,
|
||||
extension: changed_content.extension,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[napi(object)]
|
||||
pub struct ContentPathInfo {
|
||||
pub base: String,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn resolve_content_paths(args: ContentPathInfo) -> Vec<String> {
|
||||
tailwindcss_core::resolve_content_paths(tailwindcss_core::ContentPathInfo { base: args.base })
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[napi]
|
||||
pub enum IO {
|
||||
Sequential = 0b0001,
|
||||
Parallel = 0b0010,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[napi]
|
||||
pub enum Parsing {
|
||||
Sequential = 0b0100,
|
||||
Parallel = 0b1000,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn parse_candidate_strings(input: Vec<ChangedContent>, strategy: u8) -> Vec<String> {
|
||||
tailwindcss_core::parse_candidate_strings(input.into_iter().map(Into::into).collect(), strategy)
|
||||
}
|
||||
|
|
|
|||
12850
package-lock.json
generated
12850
package-lock.json
generated
File diff suppressed because it is too large
Load diff
13998
package-lock.stable.json
13998
package-lock.stable.json
File diff suppressed because it is too large
Load diff
55
package.json
55
package.json
|
|
@ -16,12 +16,9 @@
|
|||
"integrations/*",
|
||||
"oxide/crates/node"
|
||||
],
|
||||
"tailwindcss": {
|
||||
"engine": "oxide"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "concurrently -n tailwind,oxide-node -c green,yellow 'npm run dev:js' 'npm run dev:rust'",
|
||||
"build": "npm run build:rust && npm run build:js && npm run build:peers",
|
||||
"build": "npm run build:rust && npm run build:js",
|
||||
"test": "jest",
|
||||
"test:integrations": "npm run test -w ./integrations",
|
||||
"style": "eslint .",
|
||||
|
|
@ -29,8 +26,7 @@
|
|||
"dev:rust": "npm run --prefix ./oxide dev:node",
|
||||
"dev:js": "npm run build:js -- --watch",
|
||||
"build:rust": "npm run --prefix ./oxide build:node",
|
||||
"build:js": "npm run generate:plugin-list && swc src --out-dir lib --copy-files --delete-dir-on-start --config jsc.transform.optimizer.globals.vars.__OXIDE__='\"true\"'",
|
||||
"build:peers": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false",
|
||||
"build:js": "npm run generate:plugin-list && swc src --out-dir lib --copy-files --delete-dir-on-start",
|
||||
"generate:plugin-list": "node -r @swc/register scripts/create-plugin-list.js",
|
||||
"generate:types": "node -r @swc/register scripts/generate-types.js"
|
||||
},
|
||||
|
|
@ -38,7 +34,6 @@
|
|||
"src/*",
|
||||
"cli/*",
|
||||
"lib/*",
|
||||
"peers/*",
|
||||
"scripts/*.js",
|
||||
"stubs/*",
|
||||
"nesting/*",
|
||||
|
|
@ -48,57 +43,53 @@
|
|||
"*.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/core": "^1.3.55",
|
||||
"@swc/jest": "^0.2.26",
|
||||
"@swc/cli": "^0.1.63",
|
||||
"@swc/core": "^1.3.95",
|
||||
"@swc/jest": "^0.2.29",
|
||||
"@swc/register": "^0.1.10",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"concurrently": "^8.0.1",
|
||||
"cssnano": "^6.0.0",
|
||||
"esbuild": "^0.17.18",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"jest": "^29.5.0",
|
||||
"jest-diff": "^29.5.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-diff": "^29.7.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.0",
|
||||
"source-map-js": "^1.0.2",
|
||||
"turbo": "^1.9.3"
|
||||
"turbo": "^1.10.16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@tailwindcss/oxide": "file:oxide/crates/node",
|
||||
"arg": "^5.0.2",
|
||||
"browserslist": "^4.21.5",
|
||||
"browserslist": "^4.22.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.18.2",
|
||||
"lightningcss": "^1.18.0",
|
||||
"lilconfig": "^2.1.0",
|
||||
"jiti": "^1.21.0",
|
||||
"lightningcss": "^1.22.1",
|
||||
"lilconfig": "^3.0.0",
|
||||
"micromatch": "^4.0.5",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"postcss-selector-parser": "^6.0.11",
|
||||
"postcss-selector-parser": "^6.0.12",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"resolve": "^1.22.2",
|
||||
"sucrase": "^3.32.0"
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.34.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"not edge <= 18",
|
||||
"not ie 11",
|
||||
"not op_mini all"
|
||||
"defaults and supports css-variables and supports css-matches-pseudo",
|
||||
"not android <= 117",
|
||||
"safari >= 14"
|
||||
],
|
||||
"jest": {
|
||||
"testTimeout": 30000,
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
{
|
||||
"name": "tailwindcss",
|
||||
"version": "3.3.2",
|
||||
"description": "A utility-first CSS framework for rapidly building custom user interfaces.",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"repository": "https://github.com/tailwindlabs/tailwindcss.git",
|
||||
"bugs": "https://github.com/tailwindlabs/tailwindcss/issues",
|
||||
"homepage": "https://tailwindcss.com",
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
},
|
||||
"tailwindcss": {
|
||||
"engine": "stable"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "npm run generate && rimraf lib",
|
||||
"build": "swc src --out-dir lib --copy-files --config jsc.transform.optimizer.globals.vars.__OXIDE__='\"false\"'",
|
||||
"postbuild": "esbuild lib/cli-peer-dependencies.js --bundle --platform=node --outfile=peers/index.js --define:process.env.CSS_TRANSFORMER_WASM=false",
|
||||
"rebuild-fixtures": "npm run build && node -r @swc/register scripts/rebuildFixtures.js",
|
||||
"style": "eslint .",
|
||||
"pretest": "npm run generate",
|
||||
"test": "jest",
|
||||
"test:integrations": "npm run test --prefix ./integrations",
|
||||
"install:integrations": "node scripts/install-integrations.js",
|
||||
"generate:plugin-list": "node -r @swc/register scripts/create-plugin-list.js",
|
||||
"generate:types": "node -r @swc/register scripts/generate-types.js",
|
||||
"generate": "npm run generate:plugin-list && npm run generate:types",
|
||||
"release-channel": "node ./scripts/release-channel.js",
|
||||
"release-notes": "node ./scripts/release-notes.js",
|
||||
"prepublishOnly": "npm install --force && npm run build"
|
||||
},
|
||||
"files": [
|
||||
"src/*",
|
||||
"cli/*",
|
||||
"lib/*",
|
||||
"peers/*",
|
||||
"scripts/*.js",
|
||||
"stubs/*",
|
||||
"nesting/*",
|
||||
"types/**/*",
|
||||
"*.d.ts",
|
||||
"*.css",
|
||||
"*.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/core": "^1.3.55",
|
||||
"@swc/jest": "^0.2.26",
|
||||
"@swc/register": "^0.1.10",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"browserslist": "^4.21.5",
|
||||
"concurrently": "^8.0.1",
|
||||
"cssnano": "^6.0.0",
|
||||
"esbuild": "^0.17.18",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"jest": "^29.5.0",
|
||||
"jest-diff": "^29.5.0",
|
||||
"lightningcss": "1.18.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.0",
|
||||
"source-map-js": "^1.0.2",
|
||||
"turbo": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.18.2",
|
||||
"lilconfig": "^2.1.0",
|
||||
"micromatch": "^4.0.5",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"postcss-selector-parser": "^6.0.11",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"resolve": "^1.22.2",
|
||||
"sucrase": "^3.32.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"not edge <= 18",
|
||||
"not ie 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"jest": {
|
||||
"testTimeout": 30000,
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/jest/customMatchers.js"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"/integrations/",
|
||||
"/standalone-cli/",
|
||||
"\\.test\\.skip\\.js$"
|
||||
],
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!lightningcss)"
|
||||
],
|
||||
"transform": {
|
||||
"\\.js$": "@swc/jest",
|
||||
"\\.ts$": "@swc/jest"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
25
resolveConfig.d.ts
vendored
25
resolveConfig.d.ts
vendored
|
|
@ -1,11 +1,30 @@
|
|||
import type { Config, ResolvableTo } from './types/config'
|
||||
import { Config, ResolvableTo, ThemeConfig } from './types/config'
|
||||
import { DefaultTheme } from './types/generated/default-theme'
|
||||
import { DefaultColors } from './types/generated/colors'
|
||||
|
||||
type ResolvedConfig<T extends Config> = Omit<T, 'theme'> & {
|
||||
theme: MergeThemes<
|
||||
UnwrapResolvables<Omit<T['theme'], 'extend'>>,
|
||||
T['theme'] extends { extend: infer TExtend } ? UnwrapResolvables<TExtend> : {}
|
||||
>
|
||||
}
|
||||
|
||||
type UnwrapResolvables<T> = {
|
||||
[K in keyof T]: T[K] extends ResolvableTo<infer R> ? R : T[K]
|
||||
}
|
||||
|
||||
type ResolvedConfig<T extends Config> = Omit<T, 'theme'> & {
|
||||
theme: UnwrapResolvables<T['theme']>
|
||||
type ThemeConfigResolved = UnwrapResolvables<ThemeConfig>
|
||||
type DefaultThemeFull = DefaultTheme & { colors: DefaultColors }
|
||||
|
||||
type MergeThemes<Overrides extends object, Extensions extends object> = {
|
||||
[K in keyof ThemeConfigResolved | keyof Overrides]: (K extends keyof Overrides
|
||||
? Overrides[K]
|
||||
: K extends keyof DefaultThemeFull
|
||||
? DefaultThemeFull[K]
|
||||
: K extends keyof ThemeConfigResolved
|
||||
? ThemeConfigResolved[K]
|
||||
: never) &
|
||||
(K extends keyof Extensions ? Extensions[K] : {})
|
||||
}
|
||||
|
||||
declare function resolveConfig<T extends Config>(config: T): ResolvedConfig<T>
|
||||
|
|
|
|||
|
|
@ -91,9 +91,8 @@ fs.writeFileSync(
|
|||
path.join(process.cwd(), 'types', 'generated', 'default-theme.d.ts'),
|
||||
prettier.format(
|
||||
`
|
||||
import { Config } from '../../types'
|
||||
type CSSDeclarationList = Record<string, string>
|
||||
export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} }
|
||||
export type DefaultTheme = { ${defaultThemeTypes} }
|
||||
`,
|
||||
{
|
||||
semi: false,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Given a version, figure out what the release notes are so that we can use this to pre-fill the
|
||||
// relase notes on a GitHub release for the current version.
|
||||
// release notes on a GitHub release for the current version.
|
||||
|
||||
let path = require('path')
|
||||
let fs = require('fs')
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
let fs = require('fs')
|
||||
let path = require('path')
|
||||
|
||||
let engines = {
|
||||
stable: {
|
||||
files: [
|
||||
path.resolve(__dirname, '..', 'package.stable.json'),
|
||||
path.resolve(__dirname, '..', 'package-lock.stable.json'),
|
||||
],
|
||||
},
|
||||
oxide: {
|
||||
files: [
|
||||
path.resolve(__dirname, '..', 'package.oxide.json'),
|
||||
path.resolve(__dirname, '..', 'package-lock.oxide.json'),
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Find out what the current engine is that we are using:
|
||||
let [otherEngine, info] = Object.entries(engines).find(([, info]) =>
|
||||
info.files.every((file) => fs.existsSync(file))
|
||||
)
|
||||
let currentEngine = otherEngine === 'oxide' ? 'stable' : 'oxide'
|
||||
|
||||
console.log(`Current engine: \`${currentEngine}\`, swapping to \`${otherEngine}\``)
|
||||
|
||||
// Swap the engines
|
||||
for (let file of info.files) {
|
||||
fs.renameSync(
|
||||
file.replace(`.${otherEngine}`, ''),
|
||||
file.replace(`.${otherEngine}`, `.${currentEngine}`)
|
||||
)
|
||||
}
|
||||
for (let file of engines[otherEngine].files) {
|
||||
fs.renameSync(file, file.replace(`.${otherEngine}`, ''))
|
||||
}
|
||||
|
||||
console.log(
|
||||
'Engines have been swapped. Make sure to run `npm install` to update your dependencies.'
|
||||
)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
export function lazyPostcss() {
|
||||
return require('postcss')
|
||||
}
|
||||
|
||||
export function lazyPostcssImport() {
|
||||
return require('postcss-import')
|
||||
}
|
||||
|
||||
export function lazyAutoprefixer() {
|
||||
return require('autoprefixer')
|
||||
}
|
||||
|
||||
export function lazyCssnano() {
|
||||
return require('cssnano')
|
||||
}
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
if (__OXIDE__) {
|
||||
module.exports = require('./oxide/cli')
|
||||
} else {
|
||||
module.exports = require('./cli/index')
|
||||
}
|
||||
module.exports = require('./cli/index')
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
// @ts-check
|
||||
|
||||
import {
|
||||
// @ts-ignore
|
||||
lazyPostcss,
|
||||
|
||||
// @ts-ignore
|
||||
lazyPostcssImport,
|
||||
|
||||
// @ts-ignore
|
||||
lazyCssnano,
|
||||
|
||||
// @ts-ignore
|
||||
lazyAutoprefixer,
|
||||
} from '../../../peers/index.js'
|
||||
|
||||
/**
|
||||
* @returns {import('postcss')}
|
||||
*/
|
||||
export function loadPostcss() {
|
||||
// Try to load a local `postcss` version first
|
||||
try {
|
||||
return require('postcss')
|
||||
} catch {}
|
||||
|
||||
return lazyPostcss()
|
||||
}
|
||||
|
||||
export function loadPostcssImport() {
|
||||
// Try to load a local `postcss-import` version first
|
||||
try {
|
||||
return require('postcss-import')
|
||||
} catch {}
|
||||
|
||||
return lazyPostcssImport()
|
||||
}
|
||||
|
||||
export function loadCssNano() {
|
||||
let options = { preset: ['default', { cssDeclarationSorter: false }] }
|
||||
|
||||
// Try to load a local `cssnano` version first
|
||||
try {
|
||||
return require('cssnano')
|
||||
} catch {}
|
||||
|
||||
return lazyCssnano()(options)
|
||||
}
|
||||
|
||||
export function loadAutoprefixer() {
|
||||
// Try to load a local `autoprefixer` version first
|
||||
try {
|
||||
return require('autoprefixer')
|
||||
} catch {}
|
||||
|
||||
return lazyAutoprefixer()
|
||||
}
|
||||
|
|
@ -25,6 +25,10 @@ export async function build(args) {
|
|||
process.exit(9)
|
||||
}
|
||||
|
||||
if (args['--no-autoprefixer']) {
|
||||
console.error('[deprecation] The --no-autoprefixer flag is deprecated and has no effect.')
|
||||
}
|
||||
|
||||
// TODO: Reference the @config path here if exists
|
||||
let configPath = args['--config'] ? args['--config'] : resolveDefaultConfigPath()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
// @ts-check
|
||||
|
||||
import pkg from '../../../package.json'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import postcss from 'postcss'
|
||||
import postcssrc from 'postcss-load-config'
|
||||
import browserslist from 'browserslist'
|
||||
import lightning, { Features } from 'lightningcss'
|
||||
import { lilconfig } from 'lilconfig'
|
||||
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
||||
import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API
|
||||
|
||||
import tailwind from '../../processTailwindFeatures'
|
||||
import { loadAutoprefixer, loadCssNano, loadPostcss, loadPostcssImport } from './deps'
|
||||
import { formatNodes, drainStdin, outputFile } from './utils'
|
||||
import { env } from '../../lib/sharedState'
|
||||
import resolveConfig from '../../../resolveConfig.js'
|
||||
|
|
@ -19,6 +22,50 @@ import { findAtConfigPath } from '../../lib/findAtConfigPath.js'
|
|||
import log from '../../util/log'
|
||||
import { loadConfig } from '../../lib/load-config'
|
||||
import getModuleDependencies from '../../lib/getModuleDependencies'
|
||||
import { validateConfig } from '../../util/validateConfig'
|
||||
import { handleImportAtRules } from '../../lib/handleImportAtRules'
|
||||
import { flagEnabled } from '../../featureFlags'
|
||||
|
||||
function license() {
|
||||
return `/* ! tailwindcss v${pkg.version} | MIT License | https://tailwindcss.com */\n`
|
||||
}
|
||||
|
||||
async function lightningcss(result, { map = true, minify = true } = {}) {
|
||||
try {
|
||||
let resolvedBrowsersListConfig = browserslist.findConfig(
|
||||
result.opts.from ?? process.cwd()
|
||||
)?.defaults
|
||||
let defaultBrowsersListConfig = pkg.browserslist
|
||||
let browsersListConfig = resolvedBrowsersListConfig ?? defaultBrowsersListConfig
|
||||
|
||||
let transformed = lightning.transform({
|
||||
filename: result.opts.from || 'input.css',
|
||||
code: Buffer.from(result.css, 'utf-8'),
|
||||
minify,
|
||||
sourceMap: result.map === undefined ? map : !!result.map,
|
||||
inputSourceMap: result.map ? result.map.toString() : undefined,
|
||||
targets: lightning.browserslistToTargets(browserslist(browsersListConfig)),
|
||||
include: Features.Nesting,
|
||||
exclude: Features.LogicalProperties,
|
||||
})
|
||||
|
||||
return Object.assign(result, {
|
||||
css: transformed.code.toString(),
|
||||
map: result.map
|
||||
? Object.assign(result.map, {
|
||||
toString() {
|
||||
return transformed.map?.toString()
|
||||
},
|
||||
})
|
||||
: result.map,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Unable to use Lightning CSS. Using raw version instead.')
|
||||
console.error(err)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -75,39 +122,6 @@ async function loadPostCssPlugins(customPostCssPath) {
|
|||
return [beforePlugins, afterPlugins, config.options]
|
||||
}
|
||||
|
||||
function loadBuiltinPostcssPlugins() {
|
||||
let postcss = loadPostcss()
|
||||
let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
|
||||
return [
|
||||
[
|
||||
(root) => {
|
||||
root.walkAtRules('import', (rule) => {
|
||||
if (rule.params.slice(1).startsWith('tailwindcss/')) {
|
||||
rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
|
||||
rule.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
loadPostcssImport(),
|
||||
(root) => {
|
||||
root.walkComments((rule) => {
|
||||
if (rule.text.startsWith(IMPORT_COMMENT)) {
|
||||
rule.after(
|
||||
postcss.atRule({
|
||||
name: 'import',
|
||||
params: rule.text.replace(IMPORT_COMMENT, ''),
|
||||
})
|
||||
)
|
||||
rule.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
],
|
||||
[],
|
||||
{},
|
||||
]
|
||||
}
|
||||
|
||||
let state = {
|
||||
/** @type {any} */
|
||||
context: null,
|
||||
|
|
@ -118,7 +132,7 @@ let state = {
|
|||
/** @type {{content: string, extension: string}[]} */
|
||||
changedContent: [],
|
||||
|
||||
/** @type {ReturnType<typeof load> | null} */
|
||||
/** @type {{config: import('../../../types').Config, dependencies: Set<string>, dispose: Function } | null} */
|
||||
configBag: null,
|
||||
|
||||
contextDependencies: new Set(),
|
||||
|
|
@ -160,8 +174,7 @@ let state = {
|
|||
},
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.configBag.config = resolveConfig(this.configBag.config, { content: { files: [] } })
|
||||
this.configBag.config = validateConfig(resolveConfig(this.configBag.config))
|
||||
|
||||
// Override content files if `--content` has been passed explicitly
|
||||
if (content?.length > 0) {
|
||||
|
|
@ -185,7 +198,7 @@ let state = {
|
|||
let files = fastGlob.sync(this.contentPatterns.all)
|
||||
|
||||
for (let file of files) {
|
||||
if (__OXIDE__) {
|
||||
if (flagEnabled(this.config, 'oxideParser')) {
|
||||
content.push({
|
||||
file,
|
||||
extension: path.extname(file).slice(1),
|
||||
|
|
@ -211,16 +224,16 @@ let state = {
|
|||
},
|
||||
|
||||
getContext({ createContext, cliConfigPath, root, result, content }) {
|
||||
env.DEBUG && console.time('Searching for config')
|
||||
let configPath = findAtConfigPath(root, result) ?? cliConfigPath
|
||||
env.DEBUG && console.timeEnd('Searching for config')
|
||||
|
||||
if (this.context) {
|
||||
this.context.changedContent = this.changedContent.splice(0)
|
||||
|
||||
return this.context
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('Searching for config')
|
||||
let configPath = findAtConfigPath(root, result) ?? cliConfigPath
|
||||
env.DEBUG && console.timeEnd('Searching for config')
|
||||
|
||||
env.DEBUG && console.time('Loading config')
|
||||
let config = this.loadConfig(configPath, content)
|
||||
env.DEBUG && console.timeEnd('Loading config')
|
||||
|
|
@ -251,8 +264,6 @@ let state = {
|
|||
}
|
||||
|
||||
export async function createProcessor(args, cliConfigPath) {
|
||||
let postcss = loadPostcss()
|
||||
|
||||
let input = args['--input']
|
||||
let output = args['--output']
|
||||
let includePostCss = args['--postcss']
|
||||
|
|
@ -260,7 +271,9 @@ export async function createProcessor(args, cliConfigPath) {
|
|||
|
||||
let [beforePlugins, afterPlugins, postcssOptions] = includePostCss
|
||||
? await loadPostCssPlugins(customPostCssPath)
|
||||
: loadBuiltinPostcssPlugins()
|
||||
: [[], [], {}]
|
||||
|
||||
beforePlugins.unshift(...handleImportAtRules())
|
||||
|
||||
if (args['--purge']) {
|
||||
log.warn('purge-flag-deprecated', [
|
||||
|
|
@ -278,9 +291,9 @@ export async function createProcessor(args, cliConfigPath) {
|
|||
let tailwindPlugin = () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
async Once(root, { result }) {
|
||||
env.DEBUG && console.time('Compiling CSS')
|
||||
tailwind(({ createContext }) => {
|
||||
await tailwind(({ createContext }) => {
|
||||
console.error()
|
||||
console.error('Rebuilding...')
|
||||
|
||||
|
|
@ -306,8 +319,6 @@ export async function createProcessor(args, cliConfigPath) {
|
|||
tailwindPlugin,
|
||||
!args['--minify'] && formatNodes,
|
||||
...afterPlugins,
|
||||
!args['--no-autoprefixer'] && loadAutoprefixer(),
|
||||
args['--minify'] && loadCssNano(),
|
||||
].filter(Boolean)
|
||||
|
||||
/** @type {import('postcss').Processor} */
|
||||
|
|
@ -331,9 +342,20 @@ export async function createProcessor(args, cliConfigPath) {
|
|||
|
||||
async function build() {
|
||||
let start = process.hrtime.bigint()
|
||||
let options = {
|
||||
...postcssOptions,
|
||||
from: input,
|
||||
to: output,
|
||||
}
|
||||
|
||||
return readInput()
|
||||
.then((css) => processor.process(css, { ...postcssOptions, from: input, to: output }))
|
||||
.then((css) => processor.process(css, options))
|
||||
.then((result) =>
|
||||
lightningcss(result, {
|
||||
...options,
|
||||
minify: !!args['--minify'],
|
||||
})
|
||||
)
|
||||
.then((result) => {
|
||||
if (!state.watcher) {
|
||||
return result
|
||||
|
|
@ -356,12 +378,12 @@ export async function createProcessor(args, cliConfigPath) {
|
|||
})
|
||||
.then((result) => {
|
||||
if (!output) {
|
||||
process.stdout.write(result.css)
|
||||
process.stdout.write(license() + result.css)
|
||||
return
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
outputFile(result.opts.to, result.css),
|
||||
outputFile(result.opts.to, license() + result.css),
|
||||
result.map && outputFile(result.opts.to + '.map', result.map.toString()),
|
||||
])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export function createWatcher(args, { state, rebuild }) {
|
|||
// This is very likely a chokidar bug but it's one we need to work around
|
||||
// We treat this as a change event and rebuild the CSS
|
||||
watcher.on('raw', (evt, filePath, meta) => {
|
||||
if (evt !== 'rename') {
|
||||
if (evt !== 'rename' || filePath === null) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ let commands = {
|
|||
description: 'Path to a custom config file',
|
||||
},
|
||||
'--no-autoprefixer': {
|
||||
deprecated: true,
|
||||
type: Boolean,
|
||||
description: 'Disable autoprefixer',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import withAlphaVariable, { withAlphaValue } from './util/withAlphaVariable'
|
|||
import toColorValue from './util/toColorValue'
|
||||
import isPlainObject from './util/isPlainObject'
|
||||
import transformThemeValue from './util/transformThemeValue'
|
||||
import { version as tailwindVersion } from '../package.json'
|
||||
import log from './util/log'
|
||||
import {
|
||||
normalizeScreens,
|
||||
|
|
@ -22,6 +21,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow
|
|||
import { removeAlphaVariables } from './util/removeAlphaVariables'
|
||||
import { flagEnabled } from './featureFlags'
|
||||
import { normalize } from './util/dataTypes'
|
||||
import { INTERNAL_FEATURES } from './lib/setupContextUtils'
|
||||
|
||||
export let variantPlugins = {
|
||||
pseudoElementVariants: ({ addVariant }) => {
|
||||
|
|
@ -80,7 +80,7 @@ export let variantPlugins = {
|
|||
})
|
||||
},
|
||||
|
||||
pseudoClassVariants: ({ addVariant, matchVariant, config }) => {
|
||||
pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
|
||||
let pseudoVariants = [
|
||||
// Positional
|
||||
['first', '&:first-child'],
|
||||
|
|
@ -151,12 +151,12 @@ export let variantPlugins = {
|
|||
let variants = {
|
||||
group: (_, { modifier }) =>
|
||||
modifier
|
||||
? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
|
||||
: [`:merge(.group)`, ' &'],
|
||||
? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
|
||||
: [`:merge(${prefix('.group')})`, ' &'],
|
||||
peer: (_, { modifier }) =>
|
||||
modifier
|
||||
? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
|
||||
: [`:merge(.peer)`, ' ~ &'],
|
||||
? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
|
||||
: [`:merge(${prefix('.peer')})`, ' ~ &'],
|
||||
}
|
||||
|
||||
for (let [name, fn] of Object.entries(variants)) {
|
||||
|
|
@ -192,7 +192,12 @@ export let variantPlugins = {
|
|||
|
||||
return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
|
||||
},
|
||||
{ values: Object.fromEntries(pseudoVariants) }
|
||||
{
|
||||
values: Object.fromEntries(pseudoVariants),
|
||||
[INTERNAL_FEATURES]: {
|
||||
respectPrefix: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -364,7 +369,7 @@ export let variantPlugins = {
|
|||
let check = normalize(value)
|
||||
let isRaw = /^\w*\s*\(/.test(check)
|
||||
|
||||
// Chrome has a bug where `(condtion1)or(condition2)` is not valid
|
||||
// Chrome has a bug where `(condition1)or(condition2)` is not valid
|
||||
// But `(condition1) or (condition2)` is supported.
|
||||
check = isRaw ? check.replace(/\b(and|or|not)\b/g, ' $1 ') : check
|
||||
|
||||
|
|
@ -386,6 +391,26 @@ export let variantPlugins = {
|
|||
)
|
||||
},
|
||||
|
||||
hasVariants: ({ matchVariant }) => {
|
||||
matchVariant('has', (value) => `&:has(${normalize(value)})`, { values: {} })
|
||||
matchVariant(
|
||||
'group-has',
|
||||
(value, { modifier }) =>
|
||||
modifier
|
||||
? `:merge(.group\\/${modifier}):has(${normalize(value)}) &`
|
||||
: `:merge(.group):has(${normalize(value)}) &`,
|
||||
{ values: {} }
|
||||
)
|
||||
matchVariant(
|
||||
'peer-has',
|
||||
(value, { modifier }) =>
|
||||
modifier
|
||||
? `:merge(.peer\\/${modifier}):has(${normalize(value)}) ~ &`
|
||||
: `:merge(.peer):has(${normalize(value)}) ~ &`,
|
||||
{ values: {} }
|
||||
)
|
||||
},
|
||||
|
||||
ariaVariants: ({ matchVariant, theme }) => {
|
||||
matchVariant('aria', (value) => `&[aria-${normalize(value)}]`, { values: theme('aria') ?? {} })
|
||||
matchVariant(
|
||||
|
|
@ -435,15 +460,20 @@ export let variantPlugins = {
|
|||
addVariant('contrast-more', '@media (prefers-contrast: more)')
|
||||
addVariant('contrast-less', '@media (prefers-contrast: less)')
|
||||
},
|
||||
|
||||
forcedColorsVariants: ({ addVariant }) => {
|
||||
addVariant('forced-colors', '@media (forced-colors: active)')
|
||||
},
|
||||
}
|
||||
|
||||
let cssTransformValue = [
|
||||
'translate(var(--tw-translate-x), var(--tw-translate-y))',
|
||||
'rotate(var(--tw-rotate))',
|
||||
'skewX(var(--tw-skew-x))',
|
||||
'skewY(var(--tw-skew-y))',
|
||||
'scaleX(var(--tw-scale-x))',
|
||||
'scaleY(var(--tw-scale-y))',
|
||||
'var(--tw-translate-x)',
|
||||
'var(--tw-translate-y)',
|
||||
'var(--tw-rotate)',
|
||||
'var(--tw-skew-x)',
|
||||
'var(--tw-skew-y)',
|
||||
'var(--tw-scale-x)',
|
||||
'var(--tw-scale-y)',
|
||||
].join(' ')
|
||||
|
||||
let cssFilterValue = [
|
||||
|
|
@ -476,12 +506,7 @@ export let corePlugins = {
|
|||
fs.readFileSync(path.join(__dirname, './css/preflight.css'), 'utf8')
|
||||
)
|
||||
|
||||
addBase([
|
||||
postcss.comment({
|
||||
text: `! tailwindcss v${tailwindVersion} | MIT License | https://tailwindcss.com`,
|
||||
}),
|
||||
...preflightStyles.nodes,
|
||||
])
|
||||
addBase(preflightStyles.nodes)
|
||||
},
|
||||
|
||||
container: (() => {
|
||||
|
|
@ -660,6 +685,8 @@ export let corePlugins = {
|
|||
|
||||
float: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.float-start': { float: 'inline-start' },
|
||||
'.float-end': { float: 'inline-end' },
|
||||
'.float-right': { float: 'right' },
|
||||
'.float-left': { float: 'left' },
|
||||
'.float-none': { float: 'none' },
|
||||
|
|
@ -668,6 +695,8 @@ export let corePlugins = {
|
|||
|
||||
clear: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.clear-start': { clear: 'inline-start' },
|
||||
'.clear-end': { clear: 'inline-end' },
|
||||
'.clear-left': { clear: 'left' },
|
||||
'.clear-right': { clear: 'right' },
|
||||
'.clear-both': { clear: 'both' },
|
||||
|
|
@ -753,6 +782,8 @@ export let corePlugins = {
|
|||
|
||||
aspectRatio: createUtilityPlugin('aspectRatio', [['aspect', ['aspect-ratio']]]),
|
||||
|
||||
size: createUtilityPlugin('size', [['size', ['width', 'height']]]),
|
||||
|
||||
height: createUtilityPlugin('height', [['h', ['height']]]),
|
||||
maxHeight: createUtilityPlugin('maxHeight', [['max-h', ['maxHeight']]]),
|
||||
minHeight: createUtilityPlugin('minHeight', [['min-h', ['minHeight']]]),
|
||||
|
|
@ -829,72 +860,97 @@ export let corePlugins = {
|
|||
},
|
||||
|
||||
transformOrigin: createUtilityPlugin('transformOrigin', [['origin', ['transformOrigin']]]),
|
||||
translate: createUtilityPlugin(
|
||||
'translate',
|
||||
[
|
||||
[
|
||||
[
|
||||
'translate-x',
|
||||
[['@defaults transform', {}], '--tw-translate-x', ['transform', cssTransformValue]],
|
||||
],
|
||||
[
|
||||
'translate-y',
|
||||
[['@defaults transform', {}], '--tw-translate-y', ['transform', cssTransformValue]],
|
||||
],
|
||||
],
|
||||
],
|
||||
{ supportsNegativeValues: true }
|
||||
),
|
||||
rotate: createUtilityPlugin(
|
||||
'rotate',
|
||||
[['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', cssTransformValue]]]],
|
||||
{ supportsNegativeValues: true }
|
||||
),
|
||||
skew: createUtilityPlugin(
|
||||
'skew',
|
||||
[
|
||||
[
|
||||
['skew-x', [['@defaults transform', {}], '--tw-skew-x', ['transform', cssTransformValue]]],
|
||||
['skew-y', [['@defaults transform', {}], '--tw-skew-y', ['transform', cssTransformValue]]],
|
||||
],
|
||||
],
|
||||
{ supportsNegativeValues: true }
|
||||
),
|
||||
scale: createUtilityPlugin(
|
||||
'scale',
|
||||
[
|
||||
[
|
||||
'scale',
|
||||
[
|
||||
['@defaults transform', {}],
|
||||
'--tw-scale-x',
|
||||
'--tw-scale-y',
|
||||
['transform', cssTransformValue],
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'scale-x',
|
||||
[['@defaults transform', {}], '--tw-scale-x', ['transform', cssTransformValue]],
|
||||
],
|
||||
[
|
||||
'scale-y',
|
||||
[['@defaults transform', {}], '--tw-scale-y', ['transform', cssTransformValue]],
|
||||
],
|
||||
],
|
||||
],
|
||||
{ supportsNegativeValues: true }
|
||||
),
|
||||
|
||||
translate: ({ matchUtilities, theme }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
'translate-x': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-translate-x': `translateX(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
'translate-y': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-translate-y': `translateY(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
},
|
||||
{ values: theme('translate'), supportsNegativeValues: true }
|
||||
)
|
||||
},
|
||||
|
||||
rotate: ({ matchUtilities, theme }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
rotate: (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-rotate': `rotate(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
},
|
||||
{ values: theme('rotate'), supportsNegativeValues: true }
|
||||
)
|
||||
},
|
||||
|
||||
skew: ({ matchUtilities, theme }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
'skew-x': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-skew-x': `skewX(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
'skew-y': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-skew-y': `skewY(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
},
|
||||
{ values: theme('skew'), supportsNegativeValues: true }
|
||||
)
|
||||
},
|
||||
|
||||
scale: ({ matchUtilities, theme }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
'scale-x': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-scale-x': `scaleX(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
'scale-y': (value) => {
|
||||
return {
|
||||
'@defaults transform': {},
|
||||
'--tw-scale-y': `scaleY(${value})`,
|
||||
transform: cssTransformValue,
|
||||
}
|
||||
},
|
||||
},
|
||||
{ values: theme('scale'), supportsNegativeValues: true }
|
||||
)
|
||||
},
|
||||
|
||||
transform: ({ addDefaults, addUtilities }) => {
|
||||
addDefaults('transform', {
|
||||
'--tw-translate-x': '0',
|
||||
'--tw-translate-y': '0',
|
||||
'--tw-rotate': '0',
|
||||
'--tw-skew-x': '0',
|
||||
'--tw-skew-y': '0',
|
||||
'--tw-scale-x': '1',
|
||||
'--tw-scale-y': '1',
|
||||
'--tw-translate-x': ' ',
|
||||
'--tw-translate-y': ' ',
|
||||
'--tw-rotate': ' ',
|
||||
'--tw-skew-x': ' ',
|
||||
'--tw-skew-y': ' ',
|
||||
'--tw-scale-x': ' ',
|
||||
'--tw-scale-y': ' ',
|
||||
})
|
||||
|
||||
addUtilities({
|
||||
|
|
@ -913,7 +969,7 @@ export let corePlugins = {
|
|||
},
|
||||
|
||||
animation: ({ matchUtilities, theme, config }) => {
|
||||
let prefixName = (name) => `${config('prefix')}${escapeClassName(name)}`
|
||||
let prefixName = (name) => escapeClassName(config('prefix') + name)
|
||||
let keyframes = Object.fromEntries(
|
||||
Object.entries(theme('keyframes') ?? {}).map(([key, value]) => {
|
||||
return [key, { [`@keyframes ${prefixName(key)}`]: value }]
|
||||
|
|
@ -1103,6 +1159,7 @@ export let corePlugins = {
|
|||
appearance: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.appearance-none': { appearance: 'none' },
|
||||
'.appearance-auto': { appearance: 'auto' },
|
||||
})
|
||||
},
|
||||
|
||||
|
|
@ -1255,13 +1312,13 @@ export let corePlugins = {
|
|||
],
|
||||
]),
|
||||
|
||||
space: ({ matchUtilities, addUtilities, theme }) => {
|
||||
space: ({ matchUtilities, addUtilities, theme, config }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
'space-x': (value) => {
|
||||
value = value === '0' ? '0px' : value
|
||||
|
||||
if (__OXIDE__) {
|
||||
if (flagEnabled(config(), 'logicalSiblingUtilities')) {
|
||||
return {
|
||||
'& > :not([hidden]) ~ :not([hidden])': {
|
||||
'--tw-space-x-reverse': '0',
|
||||
|
|
@ -1300,13 +1357,13 @@ export let corePlugins = {
|
|||
})
|
||||
},
|
||||
|
||||
divideWidth: ({ matchUtilities, addUtilities, theme }) => {
|
||||
divideWidth: ({ matchUtilities, addUtilities, theme, config }) => {
|
||||
matchUtilities(
|
||||
{
|
||||
'divide-x': (value) => {
|
||||
value = value === '0' ? '0px' : value
|
||||
|
||||
if (__OXIDE__) {
|
||||
if (flagEnabled(config(), 'logicalSiblingUtilities')) {
|
||||
return {
|
||||
'& > :not([hidden]) ~ :not([hidden])': {
|
||||
'@defaults border-width': {},
|
||||
|
|
@ -1503,6 +1560,15 @@ export let corePlugins = {
|
|||
})
|
||||
},
|
||||
|
||||
textWrap: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.text-wrap': { 'text-wrap': 'wrap' },
|
||||
'.text-nowrap': { 'text-wrap': 'nowrap' },
|
||||
'.text-balance': { 'text-wrap': 'balance' },
|
||||
'.text-pretty': { 'text-wrap': 'pretty' },
|
||||
})
|
||||
},
|
||||
|
||||
wordBreak: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.break-normal': { 'overflow-wrap': 'normal', 'word-break': 'normal' },
|
||||
|
|
@ -2846,4 +2912,10 @@ export let corePlugins = {
|
|||
content: createUtilityPlugin('content', [
|
||||
['content', ['--tw-content', ['content', 'var(--tw-content)']]],
|
||||
]),
|
||||
forcedColorAdjust: ({ addUtilities }) => {
|
||||
addUtilities({
|
||||
'.forced-color-adjust-auto': { 'forced-color-adjust': 'auto' },
|
||||
'.forced-color-adjust-none': { 'forced-color-adjust': 'none' },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,19 @@
|
|||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
6. Use the user's configured `sans` font-variation-settings by default.
|
||||
7. Disable tap highlights on iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
html,
|
||||
:host {
|
||||
line-height: 1.5; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-moz-tab-size: 4; /* 3 */
|
||||
tab-size: 4; /* 3 */
|
||||
font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */
|
||||
font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); /* 4 */
|
||||
font-feature-settings: theme('fontFamily.sans[1].fontFeatureSettings', normal); /* 5 */
|
||||
font-variation-settings: theme('fontFamily.sans[1].fontVariationSettings', normal); /* 6 */
|
||||
-webkit-tap-highlight-color: transparent; /* 7 */
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -99,8 +102,10 @@ strong {
|
|||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
1. Use the user's configured `mono` font-family by default.
|
||||
2. Use the user's configured `mono` font-feature-settings by default.
|
||||
3. Use the user's configured `mono` font-variation-settings by default.
|
||||
4. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
|
|
@ -108,7 +113,9 @@ kbd,
|
|||
samp,
|
||||
pre {
|
||||
font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
font-feature-settings: theme('fontFamily.mono[1].fontFeatureSettings', normal); /* 2 */
|
||||
font-variation-settings: theme('fontFamily.mono[1].fontVariationSettings', normal); /* 3 */
|
||||
font-size: 1em; /* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -163,6 +170,8 @@ optgroup,
|
|||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-feature-settings: inherit; /* 1 */
|
||||
font-variation-settings: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
font-weight: inherit; /* 1 */
|
||||
line-height: inherit; /* 1 */
|
||||
|
|
@ -264,7 +273,7 @@ summary {
|
|||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
Removes the default spacing for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
|
|
@ -300,6 +309,10 @@ menu {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
dialog {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
|
@ -331,6 +344,7 @@ button,
|
|||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
|
@ -363,7 +377,10 @@ video {
|
|||
height: auto;
|
||||
}
|
||||
|
||||
/* Make elements with the HTML hidden attribute stay hidden by default */
|
||||
/*
|
||||
Make elements with the HTML hidden attribute stay hidden by default.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,27 +3,21 @@ import log from './util/log'
|
|||
|
||||
let defaults = {
|
||||
optimizeUniversalDefaults: false,
|
||||
generalizedModifiers: true,
|
||||
get disableColorOpacityUtilitiesByDefault() {
|
||||
return __OXIDE__
|
||||
},
|
||||
get relativeContentPathsByDefault() {
|
||||
return __OXIDE__
|
||||
},
|
||||
disableColorOpacityUtilitiesByDefault: false,
|
||||
relativeContentPathsByDefault: false,
|
||||
oxideParser: true,
|
||||
logicalSiblingUtilities: false,
|
||||
}
|
||||
|
||||
let featureFlags = {
|
||||
export let featureFlags = {
|
||||
future: [
|
||||
'hoverOnlyWhenSupported',
|
||||
'respectDefaultRingColorOpacity',
|
||||
'disableColorOpacityUtilitiesByDefault',
|
||||
'relativeContentPathsByDefault',
|
||||
'logicalSiblingUtilities',
|
||||
],
|
||||
experimental: [
|
||||
'optimizeUniversalDefaults',
|
||||
'generalizedModifiers',
|
||||
// 'variantGrouping',
|
||||
],
|
||||
experimental: ['optimizeUniversalDefaults', 'oxideParser'],
|
||||
}
|
||||
|
||||
export function flagEnabled(config, flag) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,40 @@ import fastGlob from 'fast-glob'
|
|||
import normalizePath from 'normalize-path'
|
||||
import { parseGlob } from '../util/parseGlob'
|
||||
import { env } from './sharedState'
|
||||
import { resolveContentPaths } from '@tailwindcss/oxide'
|
||||
|
||||
/** @typedef {import('../../types/config.js').RawFile} RawFile */
|
||||
/** @typedef {import('../../types/config.js').FilePath} FilePath */
|
||||
|
||||
/*
|
||||
* @param {import('tailwindcss').Config} tailwindConfig
|
||||
* @param {{skip:string[]}} options
|
||||
* @returns {ContentPath[]}
|
||||
*/
|
||||
function resolveContentFiles(tailwindConfig, { skip = [] } = {}) {
|
||||
if (
|
||||
Array.isArray(tailwindConfig.content.files) &&
|
||||
tailwindConfig.content.files.includes('auto')
|
||||
) {
|
||||
let idx = tailwindConfig.content.files.indexOf('auto')
|
||||
if (idx !== -1) {
|
||||
env.DEBUG && console.time('Calculating resolve content paths')
|
||||
let resolved = resolveContentPaths({ base: process.cwd() })
|
||||
env.DEBUG && console.timeEnd('Calculating resolve content paths')
|
||||
|
||||
tailwindConfig.content.files.splice(idx, 1, ...resolved)
|
||||
}
|
||||
}
|
||||
|
||||
if (skip.length > 0) {
|
||||
tailwindConfig.content.files = tailwindConfig.content.files.filter(
|
||||
(filePath) => !skip.includes(filePath)
|
||||
)
|
||||
}
|
||||
|
||||
return tailwindConfig.content.files
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} ContentPath
|
||||
* @property {string} original
|
||||
|
|
@ -32,7 +62,9 @@ import { env } from './sharedState'
|
|||
* @returns {ContentPath[]}
|
||||
*/
|
||||
export function parseCandidateFiles(context, tailwindConfig) {
|
||||
let files = tailwindConfig.content.files
|
||||
let files = resolveContentFiles(tailwindConfig, {
|
||||
skip: [context.userConfigPath],
|
||||
})
|
||||
|
||||
// Normalize the file globs
|
||||
files = files.filter((filePath) => typeof filePath === 'string')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { flagEnabled } from '../featureFlags'
|
||||
import * as regex from './regex'
|
||||
|
||||
export function defaultExtractor(context) {
|
||||
|
|
@ -12,16 +11,17 @@ export function defaultExtractor(context) {
|
|||
let results = []
|
||||
|
||||
for (let pattern of patterns) {
|
||||
results = [...results, ...(content.match(pattern) ?? [])]
|
||||
for (let result of content.match(pattern) ?? []) {
|
||||
results.push(clipAtBalancedParens(result))
|
||||
}
|
||||
}
|
||||
|
||||
return results.filter((v) => v !== undefined).map(clipAtBalancedParens)
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
function* buildRegExps(context) {
|
||||
let separator = context.tailwindConfig.separator
|
||||
let variantGroupingEnabled = flagEnabled(context.tailwindConfig, 'variantGrouping')
|
||||
let prefix =
|
||||
context.tailwindConfig.prefix !== ''
|
||||
? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
|
||||
|
|
@ -35,7 +35,7 @@ function* buildRegExps(context) {
|
|||
// This is a targeted fix to continue to allow theme()
|
||||
// with square brackets to work in arbitrary properties
|
||||
// while fixing a problem with the regex matching too much
|
||||
/\[[^\s:'"`]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
|
||||
/\[[^\s:'"`\]]+:[^\s]+?\[[^\s]+\][^\s]+?\]/,
|
||||
|
||||
// Utilities
|
||||
regex.pattern([
|
||||
|
|
@ -47,7 +47,7 @@ function* buildRegExps(context) {
|
|||
regex.any([
|
||||
regex.pattern([
|
||||
// Arbitrary values
|
||||
/-(?:\w+-)*\[[^\s:]+\]/,
|
||||
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/,
|
||||
|
||||
// Not immediately followed by an `{[(`
|
||||
/(?![{([]])/,
|
||||
|
|
@ -58,7 +58,7 @@ function* buildRegExps(context) {
|
|||
|
||||
regex.pattern([
|
||||
// Arbitrary values
|
||||
/-(?:\w+-)*\[[^\s]+\]/,
|
||||
/-(?:\w+-)*\[(?:[^\s\[\]]+\[[^\s\[\]]+\])*[^\s:\[\]]+\]/,
|
||||
|
||||
// Not immediately followed by an `{[(`
|
||||
/(?![{([]])/,
|
||||
|
|
@ -80,12 +80,18 @@ function* buildRegExps(context) {
|
|||
// This is here to provide special support for the `@` variant
|
||||
regex.pattern([/@\[[^\s"'`]+\](\/[^\s"'`]+)?/, separator]),
|
||||
|
||||
// With variant modifier (e.g.: group-[..]/modifier)
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/\w+/, separator]),
|
||||
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
|
||||
regex.pattern([/[^\s"'`\[\\]+/, separator]),
|
||||
]),
|
||||
|
||||
// With quotes allowed
|
||||
regex.any([
|
||||
// With variant modifier (e.g.: group-[..]/modifier)
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]\/\w+/, separator]),
|
||||
|
||||
regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s`]+\]/, separator]),
|
||||
regex.pattern([/[^\s`\[\\]+/, separator]),
|
||||
]),
|
||||
|
|
@ -103,15 +109,7 @@ function* buildRegExps(context) {
|
|||
|
||||
prefix,
|
||||
|
||||
variantGroupingEnabled
|
||||
? regex.any([
|
||||
// Or any of those things but grouped separated by commas
|
||||
regex.pattern([/\(/, utility, regex.zeroOrMore([/,/, utility]), /\)/]),
|
||||
|
||||
// Arbitrary properties, constrained utilities, arbitrary values, etc…
|
||||
utility,
|
||||
])
|
||||
: utility,
|
||||
utility,
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import dlv from 'dlv'
|
||||
import didYouMean from 'didyoumean'
|
||||
import transformThemeValue from '../util/transformThemeValue'
|
||||
import parseValue from 'postcss-value-parser'
|
||||
import parseValue from '../value-parser/index'
|
||||
import { normalizeScreens } from '../util/normalizeScreens'
|
||||
import buildMediaQuery from '../util/buildMediaQuery'
|
||||
import { toPath } from '../util/toPath'
|
||||
|
|
@ -146,6 +146,9 @@ function resolveVNode(node, vNode, functions) {
|
|||
}
|
||||
|
||||
function resolveFunctions(node, input, functions) {
|
||||
let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`))
|
||||
if (!hasAnyFn) return input
|
||||
|
||||
return parseValue(input)
|
||||
.walk((vNode) => {
|
||||
resolveVNode(node, vNode, functions)
|
||||
|
|
|
|||
|
|
@ -553,6 +553,13 @@ function processApply(root, context, localCache) {
|
|||
? parent.selector.slice(importantSelector.length)
|
||||
: parent.selector
|
||||
|
||||
// If the selector becomes empty after replacing the important selector
|
||||
// This means that it's the same as the parent selector and we don't want to replace it
|
||||
// Otherwise we'll crash
|
||||
if (parentSelector === '') {
|
||||
parentSelector = parent.selector
|
||||
}
|
||||
|
||||
rule.selector = replaceSelector(parentSelector, rule.selector, applyCandidate)
|
||||
|
||||
// And then re-add it if it was removed
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import fs from 'fs'
|
||||
import LRU from '@alloc/quick-lru'
|
||||
import { parseCandidateStrings, IO, Parsing } from '@tailwindcss/oxide'
|
||||
import * as sharedState from './sharedState'
|
||||
import { generateRules } from './generateRules'
|
||||
import log from '../util/log'
|
||||
import cloneNodes from '../util/cloneNodes'
|
||||
import { defaultExtractor } from './defaultExtractor'
|
||||
import { flagEnabled } from '../featureFlags'
|
||||
|
||||
let env = sharedState.env
|
||||
|
||||
|
|
@ -24,7 +26,10 @@ function getExtractor(context, fileExtension) {
|
|||
extractors[fileExtension] ||
|
||||
extractors.DEFAULT ||
|
||||
builtInExtractors[fileExtension] ||
|
||||
builtInExtractors.DEFAULT(context)
|
||||
// Because we call `DEFAULT(context)`, the returning function is always a new function without a
|
||||
// stable identity. Marking it with `DEFAULT_EXTRACTOR` allows us to check if it is the default
|
||||
// extractor without relying on the function identity.
|
||||
Object.assign(builtInExtractors.DEFAULT(context), { DEFAULT_EXTRACTOR: true })
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +103,7 @@ function buildStylesheet(rules, context) {
|
|||
}
|
||||
|
||||
export default function expandTailwindAtRules(context) {
|
||||
return (root) => {
|
||||
return async (root) => {
|
||||
let layerNodes = {
|
||||
base: null,
|
||||
components: null,
|
||||
|
|
@ -130,27 +135,51 @@ export default function expandTailwindAtRules(context) {
|
|||
|
||||
env.DEBUG && console.time('Reading changed files')
|
||||
|
||||
if (__OXIDE__) {
|
||||
// TODO: Pass through or implement `extractor`
|
||||
for (let candidate of require('@tailwindcss/oxide').parseCandidateStringsFromFiles(
|
||||
context.changedContent
|
||||
// Object.assign({}, builtInTransformers, context.tailwindConfig.content.transform)
|
||||
/** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */
|
||||
let regexParserContent = []
|
||||
|
||||
/** @type {{file?: string, content?: string}[]} */
|
||||
let rustParserContent = []
|
||||
|
||||
for (let item of context.changedContent) {
|
||||
let transformer = getTransformer(context.tailwindConfig, item.extension)
|
||||
let extractor = getExtractor(context, item.extension)
|
||||
|
||||
if (
|
||||
flagEnabled(context.tailwindConfig, 'oxideParser') &&
|
||||
transformer === builtInTransformers.DEFAULT &&
|
||||
extractor?.DEFAULT_EXTRACTOR === true
|
||||
) {
|
||||
rustParserContent.push(item)
|
||||
} else {
|
||||
regexParserContent.push([item, { transformer, extractor }])
|
||||
}
|
||||
}
|
||||
|
||||
// Read files using our newer, faster parser when:
|
||||
// - Oxide is enabled; AND
|
||||
// - The file is using default transfomers and extractors
|
||||
if (rustParserContent.length > 0) {
|
||||
for (let candidate of parseCandidateStrings(
|
||||
rustParserContent,
|
||||
IO.Parallel | Parsing.Parallel
|
||||
)) {
|
||||
candidates.add(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
// for (let { file, content, extension } of context.changedContent) {
|
||||
// let transformer = getTransformer(context.tailwindConfig, extension)
|
||||
// let extractor = getExtractor(context, extension)
|
||||
// getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
|
||||
// }
|
||||
} else {
|
||||
for (let { file, content, extension } of context.changedContent) {
|
||||
let transformer = getTransformer(context.tailwindConfig, extension)
|
||||
let extractor = getExtractor(context, extension)
|
||||
content = file ? fs.readFileSync(file, 'utf8') : content
|
||||
getClassCandidates(transformer(content), extractor, candidates, seen)
|
||||
}
|
||||
// Otherwise, read any files in node and parse with regexes
|
||||
const BATCH_SIZE = 500
|
||||
|
||||
for (let i = 0; i < regexParserContent.length; i += BATCH_SIZE) {
|
||||
let batch = regexParserContent.slice(i, i + BATCH_SIZE)
|
||||
|
||||
await Promise.all(
|
||||
batch.map(async ([{ file, content }, { transformer, extractor }]) => {
|
||||
content = file ? await fs.promises.readFile(file, 'utf8') : content
|
||||
getClassCandidates(transformer(content), extractor, candidates, seen)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
env.DEBUG && console.timeEnd('Reading changed files')
|
||||
|
|
@ -162,15 +191,16 @@ export default function expandTailwindAtRules(context) {
|
|||
|
||||
env.DEBUG && console.time('Generate rules')
|
||||
env.DEBUG && console.time('Sorting candidates')
|
||||
let sortedCandidates = __OXIDE__
|
||||
? candidates
|
||||
: new Set(
|
||||
[...candidates].sort((a, z) => {
|
||||
if (a === z) return 0
|
||||
if (a < z) return -1
|
||||
return 1
|
||||
})
|
||||
)
|
||||
// TODO: only sort if we are not using the oxide parser (flagEnabled(context.tailwindConfig,
|
||||
// 'oxideParser')) AND if we got all the candidates form the oxideParser alone. This will not
|
||||
// be the case currently if you have custom transformers / extractors.
|
||||
let sortedCandidates = new Set(
|
||||
[...candidates].sort((a, z) => {
|
||||
if (a === z) return 0
|
||||
if (a < z) return -1
|
||||
return 1
|
||||
})
|
||||
)
|
||||
env.DEBUG && console.timeEnd('Sorting candidates')
|
||||
generateRules(sortedCandidates, context)
|
||||
env.DEBUG && console.timeEnd('Generate rules')
|
||||
|
|
@ -244,13 +274,26 @@ export default function expandTailwindAtRules(context) {
|
|||
)
|
||||
layerNodes.variants.remove()
|
||||
} else if (variantNodes.length > 0) {
|
||||
root.append(
|
||||
cloneNodes(variantNodes, root.source, {
|
||||
layer: 'variants',
|
||||
let cloned = cloneNodes(variantNodes, undefined, {
|
||||
layer: 'variants',
|
||||
})
|
||||
|
||||
cloned.forEach((node) => {
|
||||
let parentLayer = node.raws.tailwind?.parentLayer ?? null
|
||||
|
||||
node.walk((n) => {
|
||||
if (!n.source) {
|
||||
n.source = layerNodes[parentLayer].source
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
root.append(cloned)
|
||||
}
|
||||
|
||||
// TODO: Why is the root node having no source location for `end` possible?
|
||||
root.source.end = root.source.end ?? root.source.start
|
||||
|
||||
// If we've got a utility layer and no utilities are generated there's likely something wrong
|
||||
const hasUtilityVariants = variantNodes.some(
|
||||
(node) => node.raws.tailwind?.parentLayer === 'utilities'
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@ import {
|
|||
} from '../util/formatVariantSelector'
|
||||
import { asClass } from '../util/nameClass'
|
||||
import { normalize } from '../util/dataTypes'
|
||||
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
||||
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
|
||||
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
|
||||
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
|
||||
import { flagEnabled } from '../featureFlags'
|
||||
import { applyImportantSelector } from '../util/applyImportantSelector'
|
||||
|
||||
let classNameParser = selectorParser((selectors) => {
|
||||
|
|
@ -45,7 +44,7 @@ function* candidatePermutations(candidate) {
|
|||
let bracketIdx = candidate.indexOf('[')
|
||||
|
||||
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
||||
// eg. string[]
|
||||
// e.g. string[]
|
||||
if (candidate[bracketIdx - 1] === '-') {
|
||||
dashIdx = bracketIdx - 1
|
||||
} else if (candidate[bracketIdx - 1] === '/') {
|
||||
|
|
@ -175,10 +174,6 @@ function applyVariant(variant, matches, context) {
|
|||
if (modifiers.length && !context.variantMap.has(variant)) {
|
||||
variant = baseVariant
|
||||
args.modifier = modifiers[0]
|
||||
|
||||
if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -193,13 +188,13 @@ function applyVariant(variant, matches, context) {
|
|||
// group[:hover] (`-` is missing)
|
||||
let match = /(.)(-?)\[(.*)\]/g.exec(variant)
|
||||
if (match) {
|
||||
let [, char, seperator, value] = match
|
||||
let [, char, separator, value] = match
|
||||
// @-[200px] case
|
||||
if (char === '@' && seperator === '-') return []
|
||||
if (char === '@' && separator === '-') return []
|
||||
// group[:hover] case
|
||||
if (char !== '@' && seperator === '') return []
|
||||
if (char !== '@' && separator === '') return []
|
||||
|
||||
variant = variant.replace(`${seperator}[${value}]`, '')
|
||||
variant = variant.replace(`${separator}[${value}]`, '')
|
||||
args.value = value
|
||||
}
|
||||
}
|
||||
|
|
@ -230,9 +225,16 @@ function applyVariant(variant, matches, context) {
|
|||
|
||||
if (context.variantMap.has(variant)) {
|
||||
let isArbitraryVariant = isArbitraryValue(variant)
|
||||
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
|
||||
let variantFunctionTuples = context.variantMap.get(variant).slice()
|
||||
let result = []
|
||||
|
||||
let respectPrefix = (() => {
|
||||
if (isArbitraryVariant) return false
|
||||
if (internalFeatures.respectPrefix === false) return false
|
||||
return true
|
||||
})()
|
||||
|
||||
for (let [meta, rule] of matches) {
|
||||
// Don't generate variants for user css
|
||||
if (meta.layer === 'user') {
|
||||
|
|
@ -293,7 +295,7 @@ function applyVariant(variant, matches, context) {
|
|||
format(selectorFormat) {
|
||||
collectedFormats.push({
|
||||
format: selectorFormat,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
},
|
||||
args,
|
||||
|
|
@ -322,7 +324,7 @@ function applyVariant(variant, matches, context) {
|
|||
if (typeof ruleWithVariant === 'string') {
|
||||
collectedFormats.push({
|
||||
format: ruleWithVariant,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +368,7 @@ function applyVariant(variant, matches, context) {
|
|||
// format: .foo &
|
||||
collectedFormats.push({
|
||||
format: modified.replace(rebuiltBase, '&'),
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
})
|
||||
rule.selector = before
|
||||
})
|
||||
|
|
@ -459,9 +461,9 @@ function isParsableNode(node) {
|
|||
}
|
||||
|
||||
function isParsableCssValue(property, value) {
|
||||
// We don't want to to treat [https://example.com] as a custom property
|
||||
// We don't want to treat [https://example.com] as a custom property
|
||||
// Even though, according to the CSS grammar, it's a totally valid CSS declaration
|
||||
// So we short-circuit here by checking if the custom property looks like a url
|
||||
// So we short-circuit here by checking if the custom property looks like a URL
|
||||
if (looksLikeUri(`${property}:${value}`)) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -489,7 +491,7 @@ function extractArbitraryProperty(classCandidate, context) {
|
|||
return null
|
||||
}
|
||||
|
||||
let normalized = normalize(value)
|
||||
let normalized = normalize(value, { property })
|
||||
|
||||
if (!isParsableCssValue(property, normalized)) {
|
||||
return null
|
||||
|
|
@ -566,7 +568,7 @@ function* recordCandidates(matches, classCandidate) {
|
|||
}
|
||||
}
|
||||
|
||||
function* resolveMatches(candidate, context, original = candidate) {
|
||||
function* resolveMatches(candidate, context) {
|
||||
let separator = context.tailwindConfig.separator
|
||||
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
||||
let important = false
|
||||
|
|
@ -576,15 +578,6 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|||
classCandidate = classCandidate.slice(1)
|
||||
}
|
||||
|
||||
if (flagEnabled(context.tailwindConfig, 'variantGrouping')) {
|
||||
if (classCandidate.startsWith('(') && classCandidate.endsWith(')')) {
|
||||
let base = variants.slice().reverse().join(separator)
|
||||
for (let part of splitAtTopLevelOnly(classCandidate.slice(1, -1), ',')) {
|
||||
yield* resolveMatches(base + separator + part, context, original)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Reintroduce this in ways that doesn't break on false positives
|
||||
// function sortAgainst(toSort, against) {
|
||||
// return toSort.slice().sort((a, z) => {
|
||||
|
|
@ -773,7 +766,7 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|||
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
||||
|
||||
// Apply final format selector
|
||||
match = applyFinalFormat(match, { context, candidate, original })
|
||||
match = applyFinalFormat(match, { context, candidate })
|
||||
|
||||
// Skip rules with invalid selectors
|
||||
// This will cause the candidate to be added to the "not class"
|
||||
|
|
@ -787,7 +780,7 @@ function* resolveMatches(candidate, context, original = candidate) {
|
|||
}
|
||||
}
|
||||
|
||||
function applyFinalFormat(match, { context, candidate, original }) {
|
||||
function applyFinalFormat(match, { context, candidate }) {
|
||||
if (!match[0].collectedFormats) {
|
||||
return match
|
||||
}
|
||||
|
|
@ -822,10 +815,19 @@ function applyFinalFormat(match, { context, candidate, original }) {
|
|||
}
|
||||
|
||||
try {
|
||||
rule.selector = finalizeSelector(rule.selector, finalFormat, {
|
||||
candidate: original,
|
||||
let selector = finalizeSelector(rule.selector, finalFormat, {
|
||||
candidate,
|
||||
context,
|
||||
})
|
||||
|
||||
// Finalize Selector determined that this candidate is irrelevant
|
||||
// TODO: This elimination should happen earlier so this never happens
|
||||
if (selector === null) {
|
||||
rule.remove()
|
||||
return
|
||||
}
|
||||
|
||||
rule.selector = selector
|
||||
} catch {
|
||||
// If this selector is invalid we also want to skip it
|
||||
// But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
|
||||
|
|
@ -838,6 +840,11 @@ function applyFinalFormat(match, { context, candidate, original }) {
|
|||
return null
|
||||
}
|
||||
|
||||
// If all rules have been eliminated we can skip this candidate entirely
|
||||
if (container.nodes.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
match[1] = container.nodes[0]
|
||||
|
||||
return match
|
||||
|
|
@ -875,7 +882,7 @@ function getImportantStrategy(important) {
|
|||
}
|
||||
}
|
||||
|
||||
function generateRules(candidates, context) {
|
||||
function generateRules(candidates, context, isSorting = false) {
|
||||
let allRules = []
|
||||
let strategy = getImportantStrategy(context.tailwindConfig.important)
|
||||
|
||||
|
|
@ -910,7 +917,9 @@ function generateRules(candidates, context) {
|
|||
rule = container.nodes[0]
|
||||
}
|
||||
|
||||
let newEntry = [sort, rule]
|
||||
// Note: We have to clone rules during sorting
|
||||
// so we eliminate some shared mutable state
|
||||
let newEntry = [sort, isSorting ? rule.clone() : rule]
|
||||
rules.add(newEntry)
|
||||
context.ruleCache.add(newEntry)
|
||||
allRules.push(newEntry)
|
||||
|
|
|
|||
34
src/lib/handleImportAtRules.js
Normal file
34
src/lib/handleImportAtRules.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import postcss from 'postcss'
|
||||
import postcssImport from 'postcss-import'
|
||||
|
||||
const TAILWIND = Symbol()
|
||||
|
||||
export function handleImportAtRules() {
|
||||
let RESTORE_ATRULE_COMMENT = '__TAILWIND_RESTORE__'
|
||||
let atRulesToRestore = ['tailwind', 'config']
|
||||
|
||||
return [
|
||||
(root) => {
|
||||
root.walkAtRules((rule) => {
|
||||
if (!atRulesToRestore.includes(rule.name)) return rule
|
||||
|
||||
rule.after(
|
||||
postcss.comment({
|
||||
text: RESTORE_ATRULE_COMMENT,
|
||||
raws: { [TAILWIND]: { rule } },
|
||||
})
|
||||
)
|
||||
rule.remove()
|
||||
})
|
||||
},
|
||||
postcssImport(),
|
||||
(root) => {
|
||||
root.walkComments((rule) => {
|
||||
if (rule.text === RESTORE_ATRULE_COMMENT) {
|
||||
rule.after(rule.raws[TAILWIND].rule)
|
||||
rule.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -4,6 +4,14 @@ import { transform } from 'sucrase'
|
|||
import { Config } from '../../types/config'
|
||||
|
||||
let jiti: ReturnType<typeof jitiFactory> | null = null
|
||||
|
||||
// @internal
|
||||
// This WILL be removed in some future release
|
||||
// If you rely on this your stuff WILL break
|
||||
export function useCustomJiti(_jiti: ReturnType<typeof jitiFactory>) {
|
||||
jiti = _jiti
|
||||
}
|
||||
|
||||
function lazyJiti() {
|
||||
return (
|
||||
jiti ??
|
||||
|
|
|
|||
|
|
@ -10,33 +10,6 @@ export default function normalizeTailwindDirectives(root) {
|
|||
applyDirectives.add(atRule)
|
||||
}
|
||||
|
||||
if (atRule.name === 'import') {
|
||||
if (atRule.params === '"tailwindcss/base"' || atRule.params === "'tailwindcss/base'") {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'base'
|
||||
} else if (
|
||||
atRule.params === '"tailwindcss/components"' ||
|
||||
atRule.params === "'tailwindcss/components'"
|
||||
) {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'components'
|
||||
} else if (
|
||||
atRule.params === '"tailwindcss/utilities"' ||
|
||||
atRule.params === "'tailwindcss/utilities'"
|
||||
) {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'utilities'
|
||||
} else if (
|
||||
atRule.params === '"tailwindcss/screens"' ||
|
||||
atRule.params === "'tailwindcss/screens'" ||
|
||||
atRule.params === '"tailwindcss/variants"' ||
|
||||
atRule.params === "'tailwindcss/variants'"
|
||||
) {
|
||||
atRule.name = 'tailwind'
|
||||
atRule.params = 'variants'
|
||||
}
|
||||
}
|
||||
|
||||
if (atRule.name === 'tailwind') {
|
||||
if (atRule.params === 'screens') {
|
||||
atRule.params = 'variants'
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { remapBitfield } from './remap-bitfield.js'
|
|||
* @property {bigint} arbitrary 0n if false, 1n if true
|
||||
* @property {bigint} variants Dynamic size. 1 bit per registered variant. 0n means no variants
|
||||
* @property {bigint} parallelIndex Rule index for the parallel variant. 0 if not applicable.
|
||||
* @property {bigint} index Index of the rule / utility in it's given *parent* layer. Monotonically increasing.
|
||||
* @property {bigint} index Index of the rule / utility in its given *parent* layer. Monotonically increasing.
|
||||
* @property {VariantOption[]} options Some information on how we can sort arbitrary variants
|
||||
*/
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ export class Offsets {
|
|||
let mapping = this.recalculateVariantOffsets()
|
||||
|
||||
// No arbitrary variants? Nothing to do.
|
||||
// Everyhing already in order? Nothing to do.
|
||||
// Everything already in order? Nothing to do.
|
||||
if (mapping.length === 0) {
|
||||
return list
|
||||
}
|
||||
|
|
@ -354,7 +354,7 @@ function max(nums) {
|
|||
* Using `.sort()` without a custom compare function is faster
|
||||
* But you can only use that if you're sorting an array of
|
||||
* only strings. If you're sorting strings inside objects
|
||||
* or arrays, you need must use a custom compare function.
|
||||
* or arrays, you need to use a custom compare function.
|
||||
*
|
||||
* @param {string} a
|
||||
* @param {string} b
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ import isSyntacticallyValidPropertyValue from '../util/isSyntacticallyValidPrope
|
|||
import { generateRules, getClassNameFromSelector } from './generateRules'
|
||||
import { hasContentChanged } from './cacheInvalidation.js'
|
||||
import { Offsets } from './offsets.js'
|
||||
import { flagEnabled } from '../featureFlags.js'
|
||||
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
|
||||
|
||||
export const INTERNAL_FEATURES = Symbol()
|
||||
|
||||
const VARIANT_TYPES = {
|
||||
AddVariant: Symbol.for('ADD_VARIANT'),
|
||||
MatchVariant: Symbol.for('MATCH_VARIANT'),
|
||||
|
|
@ -146,43 +147,45 @@ function getClasses(selector, mutate) {
|
|||
return parser.transformSync(selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore everything inside a :not(...). This allows you to write code like
|
||||
* `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
||||
* not generated it. But now we will ignore everything inside a `:not`, so
|
||||
* that it still gets generated.
|
||||
*
|
||||
* @param {selectorParser.Root} selectors
|
||||
*/
|
||||
function ignoreNot(selectors) {
|
||||
selectors.walkPseudos((pseudo) => {
|
||||
if (pseudo.value === ':not') {
|
||||
pseudo.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function extractCandidates(node, state = { containsNonOnDemandable: false }, depth = 0) {
|
||||
let classes = []
|
||||
let selectors = []
|
||||
|
||||
// Handle normal rules
|
||||
if (node.type === 'rule') {
|
||||
// Ignore everything inside a :not(...). This allows you to write code like
|
||||
// `div:not(.foo)`. If `.foo` is never found in your code, then we used to
|
||||
// not generated it. But now we will ignore everything inside a `:not`, so
|
||||
// that it still gets generated.
|
||||
function ignoreNot(selectors) {
|
||||
selectors.walkPseudos((pseudo) => {
|
||||
if (pseudo.value === ':not') {
|
||||
pseudo.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for (let selector of node.selectors) {
|
||||
let classCandidates = getClasses(selector, ignoreNot)
|
||||
// At least one of the selectors contains non-"on-demandable" candidates.
|
||||
if (classCandidates.length === 0) {
|
||||
state.containsNonOnDemandable = true
|
||||
}
|
||||
|
||||
for (let classCandidate of classCandidates) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
}
|
||||
// Handle normal rules
|
||||
selectors.push(...node.selectors)
|
||||
} else if (node.type === 'atrule') {
|
||||
// Handle at-rules (which contains nested rules)
|
||||
node.walkRules((rule) => selectors.push(...rule.selectors))
|
||||
}
|
||||
|
||||
// Handle at-rules (which contains nested rules)
|
||||
else if (node.type === 'atrule') {
|
||||
node.walkRules((rule) => {
|
||||
for (let classCandidate of rule.selectors.flatMap((selector) => getClasses(selector))) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
})
|
||||
for (let selector of selectors) {
|
||||
let classCandidates = getClasses(selector, ignoreNot)
|
||||
|
||||
// At least one of the selectors contains non-"on-demandable" candidates.
|
||||
if (classCandidates.length === 0) {
|
||||
state.containsNonOnDemandable = true
|
||||
}
|
||||
|
||||
for (let classCandidate of classCandidates) {
|
||||
classes.push(classCandidate)
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
|
|
@ -230,8 +233,8 @@ export function parseVariant(variant) {
|
|||
return ({ format }) => format(str)
|
||||
}
|
||||
|
||||
let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str)
|
||||
return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() }))
|
||||
let [, name, params] = /@(\S*)( .+|[({].*)?/g.exec(str)
|
||||
return ({ wrap }) => wrap(postcss.atRule({ name, params: params?.trim() ?? '' }))
|
||||
})
|
||||
.reverse()
|
||||
|
||||
|
|
@ -434,10 +437,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
},
|
||||
}
|
||||
|
||||
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
||||
|
||||
let ruleSets = []
|
||||
.concat(modifiersEnabled ? rule(value, extras) : rule(value))
|
||||
.concat(rule(value, extras))
|
||||
.filter(Boolean)
|
||||
.map((declaration) => ({
|
||||
[nameClass(identifier, modifier)]: declaration,
|
||||
|
|
@ -514,10 +515,8 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
},
|
||||
}
|
||||
|
||||
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
||||
|
||||
let ruleSets = []
|
||||
.concat(modifiersEnabled ? rule(value, extras) : rule(value))
|
||||
.concat(rule(value, extras))
|
||||
.filter(Boolean)
|
||||
.map((declaration) => ({
|
||||
[nameClass(identifier, modifier)]: declaration,
|
||||
|
|
@ -585,18 +584,13 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
let id = options?.id ?? ++variantIdentifier
|
||||
let isSpecial = variant === '@'
|
||||
|
||||
let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
|
||||
|
||||
for (let [key, value] of Object.entries(options?.values ?? {})) {
|
||||
if (key === 'DEFAULT') continue
|
||||
|
||||
api.addVariant(
|
||||
isSpecial ? `${variant}${key}` : `${variant}-${key}`,
|
||||
({ args, container }) => {
|
||||
return variantFn(
|
||||
value,
|
||||
modifiersEnabled ? { modifier: args?.modifier, container } : { container }
|
||||
)
|
||||
return variantFn(value, { modifier: args?.modifier, container })
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -624,7 +618,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
: // Falling back to args if it is a string, otherwise '' for older intellisense
|
||||
// (JetBrains) plugins.
|
||||
args?.value ?? (typeof args === 'string' ? args : ''),
|
||||
modifiersEnabled ? { modifier: args?.modifier, container } : { container }
|
||||
{ modifier: args?.modifier, container }
|
||||
)
|
||||
},
|
||||
{
|
||||
|
|
@ -729,7 +723,7 @@ function collectLayerPlugins(root) {
|
|||
}
|
||||
|
||||
function resolvePlugins(context, root) {
|
||||
let corePluginList = Object.entries({ ...variantPlugins, ...corePlugins })
|
||||
let corePluginList = Object.entries(corePlugins)
|
||||
.map(([name, plugin]) => {
|
||||
if (!context.tailwindConfig.corePlugins.includes(name)) {
|
||||
return null
|
||||
|
|
@ -754,6 +748,7 @@ function resolvePlugins(context, root) {
|
|||
let beforeVariants = [
|
||||
variantPlugins['pseudoElementVariants'],
|
||||
variantPlugins['pseudoClassVariants'],
|
||||
variantPlugins['hasVariants'],
|
||||
variantPlugins['ariaVariants'],
|
||||
variantPlugins['dataVariants'],
|
||||
]
|
||||
|
|
@ -762,6 +757,7 @@ function resolvePlugins(context, root) {
|
|||
variantPlugins['directionVariants'],
|
||||
variantPlugins['reducedMotionVariants'],
|
||||
variantPlugins['prefersContrastVariants'],
|
||||
variantPlugins['forcedColorsVariants'],
|
||||
variantPlugins['darkVariants'],
|
||||
variantPlugins['printVariant'],
|
||||
variantPlugins['screenVariants'],
|
||||
|
|
@ -943,13 +939,17 @@ function registerPlugins(plugins, context) {
|
|||
|
||||
// Sort all classes in order
|
||||
// Non-tailwind classes won't be generated and will be left as `null`
|
||||
let rules = generateRules(new Set(sorted), context)
|
||||
let rules = generateRules(new Set(sorted), context, true)
|
||||
rules = context.offsets.sort(rules)
|
||||
|
||||
let idx = BigInt(parasiteUtilities.length)
|
||||
|
||||
for (const [, rule] of rules) {
|
||||
sortedClassNames.set(rule.raws.tailwind.candidate, idx++)
|
||||
let candidate = rule.raws.tailwind.candidate
|
||||
|
||||
// When multiple rules match a candidate
|
||||
// always take the position of the first one
|
||||
sortedClassNames.set(candidate, sortedClassNames.get(candidate) ?? idx++)
|
||||
}
|
||||
|
||||
return classes.map((className) => {
|
||||
|
|
@ -1119,17 +1119,24 @@ function registerPlugins(plugins, context) {
|
|||
}
|
||||
|
||||
let isArbitraryVariant = !(value in (options.values ?? {}))
|
||||
let internalFeatures = options[INTERNAL_FEATURES] ?? {}
|
||||
|
||||
let respectPrefix = (() => {
|
||||
if (isArbitraryVariant) return false
|
||||
if (internalFeatures.respectPrefix === false) return false
|
||||
return true
|
||||
})()
|
||||
|
||||
formatStrings = formatStrings.map((format) =>
|
||||
format.map((str) => ({
|
||||
format: str,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
}))
|
||||
)
|
||||
|
||||
manualFormatStrings = manualFormatStrings.map((format) => ({
|
||||
format,
|
||||
isArbitraryVariant,
|
||||
respectPrefix,
|
||||
}))
|
||||
|
||||
let opts = {
|
||||
|
|
|
|||
|
|
@ -63,9 +63,7 @@ function getTailwindConfig(configOrPath) {
|
|||
}
|
||||
|
||||
// It's a plain object, not a path
|
||||
let newConfig = resolveConfig(
|
||||
configOrPath.config === undefined ? configOrPath : configOrPath.config
|
||||
)
|
||||
let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {})
|
||||
|
||||
newConfig = validateConfig(newConfig)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
import pkg from '../../package.json'
|
||||
|
||||
export const env =
|
||||
typeof process !== 'undefined'
|
||||
? {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
DEBUG: resolveDebug(process.env.DEBUG),
|
||||
ENGINE: pkg.tailwindcss.engine,
|
||||
}
|
||||
: {
|
||||
NODE_ENV: 'production',
|
||||
DEBUG: false,
|
||||
ENGINE: pkg.tailwindcss.engine,
|
||||
}
|
||||
|
||||
export const contextMap = new Map()
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
import './cli/index'
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import packageJson from '../../../../package.json'
|
||||
import browserslist from 'browserslist'
|
||||
import { Result } from 'postcss'
|
||||
|
||||
import {
|
||||
// @ts-ignore
|
||||
lazyPostcss,
|
||||
|
||||
// @ts-ignore
|
||||
lazyPostcssImport,
|
||||
|
||||
// @ts-ignore
|
||||
lazyCssnano,
|
||||
|
||||
// @ts-ignore
|
||||
} from '../../../../peers/index'
|
||||
|
||||
export function lazyLightningCss() {
|
||||
// TODO: Make this lazy/bundled
|
||||
return require('lightningcss')
|
||||
}
|
||||
|
||||
let lightningCss
|
||||
|
||||
function loadLightningCss() {
|
||||
if (lightningCss) {
|
||||
return lightningCss
|
||||
}
|
||||
|
||||
// Try to load a local version first
|
||||
try {
|
||||
return (lightningCss = require('lightningcss'))
|
||||
} catch {}
|
||||
|
||||
return (lightningCss = lazyLightningCss())
|
||||
}
|
||||
|
||||
export async function lightningcss(shouldMinify: boolean, result: Result) {
|
||||
let css = loadLightningCss()
|
||||
|
||||
try {
|
||||
let transformed = css.transform({
|
||||
filename: result.opts.from || 'input.css',
|
||||
code: Buffer.from(result.css, 'utf-8'),
|
||||
minify: shouldMinify,
|
||||
sourceMap: !!result.map,
|
||||
inputSourceMap: result.map ? result.map.toString() : undefined,
|
||||
targets: css.browserslistToTargets(browserslist(packageJson.browserslist)),
|
||||
drafts: {
|
||||
nesting: true,
|
||||
},
|
||||
})
|
||||
|
||||
return Object.assign(result, {
|
||||
css: transformed.code.toString('utf8'),
|
||||
map: result.map
|
||||
? Object.assign(result.map, {
|
||||
toString() {
|
||||
return transformed.map.toString()
|
||||
},
|
||||
})
|
||||
: result.map,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Unable to use Lightning CSS. Using raw version instead.')
|
||||
console.error(err)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {import('postcss')}
|
||||
*/
|
||||
export function loadPostcss() {
|
||||
// Try to load a local `postcss` version first
|
||||
try {
|
||||
return require('postcss')
|
||||
} catch {}
|
||||
|
||||
return lazyPostcss()
|
||||
}
|
||||
|
||||
export function loadPostcssImport() {
|
||||
// Try to load a local `postcss-import` version first
|
||||
try {
|
||||
return require('postcss-import')
|
||||
} catch {}
|
||||
|
||||
return lazyPostcssImport()
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { resolveDefaultConfigPath } from '../../../util/resolveConfigPath'
|
||||
import { createProcessor } from './plugin'
|
||||
|
||||
export async function build(args) {
|
||||
let input = args['--input']
|
||||
let shouldWatch = args['--watch']
|
||||
|
||||
// TODO: Deprecate this in future versions
|
||||
if (!input && args['_'][1]) {
|
||||
console.error('[deprecation] Running tailwindcss without -i, please provide an input file.')
|
||||
input = args['--input'] = args['_'][1]
|
||||
}
|
||||
|
||||
if (input && input !== '-' && !fs.existsSync((input = path.resolve(input)))) {
|
||||
console.error(`Specified input file ${args['--input']} does not exist.`)
|
||||
process.exit(9)
|
||||
}
|
||||
|
||||
if (args['--config'] && !fs.existsSync((args['--config'] = path.resolve(args['--config'])))) {
|
||||
console.error(`Specified config file ${args['--config']} does not exist.`)
|
||||
process.exit(9)
|
||||
}
|
||||
|
||||
// TODO: Reference the @config path here if exists
|
||||
let configPath = args['--config'] ? args['--config'] : resolveDefaultConfigPath()
|
||||
|
||||
let processor = await createProcessor(args, configPath)
|
||||
|
||||
if (shouldWatch) {
|
||||
// Abort the watcher if stdin is closed to avoid zombie processes
|
||||
// You can disable this behavior with --watch=always
|
||||
if (args['--watch'] !== 'always') {
|
||||
process.stdin.on('end', () => process.exit(0))
|
||||
}
|
||||
|
||||
process.stdin.resume()
|
||||
|
||||
await processor.watch()
|
||||
} else {
|
||||
await processor.build().catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,442 +0,0 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import postcssrc from 'postcss-load-config'
|
||||
import { lilconfig } from 'lilconfig'
|
||||
import loadPlugins from 'postcss-load-config/src/plugins' // Little bit scary, looking at private/internal API
|
||||
import loadOptions from 'postcss-load-config/src/options' // Little bit scary, looking at private/internal API
|
||||
|
||||
import tailwind from '../../../processTailwindFeatures'
|
||||
import { loadPostcss, loadPostcssImport, lightningcss } from './deps'
|
||||
import { formatNodes, drainStdin, outputFile } from './utils'
|
||||
import { env } from '../../../lib/sharedState'
|
||||
import resolveConfig from '../../../../resolveConfig'
|
||||
import { parseCandidateFiles } from '../../../lib/content'
|
||||
import { createWatcher } from './watching'
|
||||
import fastGlob from 'fast-glob'
|
||||
import { findAtConfigPath } from '../../../lib/findAtConfigPath'
|
||||
import log from '../../../util/log'
|
||||
import { loadConfig } from '../../../lib/load-config'
|
||||
import getModuleDependencies from '../../../lib/getModuleDependencies'
|
||||
import type { Config } from '../../../../types'
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} [customPostCssPath ]
|
||||
* @returns
|
||||
*/
|
||||
async function loadPostCssPlugins(customPostCssPath) {
|
||||
let config = customPostCssPath
|
||||
? await (async () => {
|
||||
let file = path.resolve(customPostCssPath)
|
||||
|
||||
// Implementation, see: https://unpkg.com/browse/postcss-load-config@3.1.0/src/index.js
|
||||
// @ts-ignore
|
||||
let { config = {} } = await lilconfig('postcss').load(file)
|
||||
if (typeof config === 'function') {
|
||||
config = config()
|
||||
} else {
|
||||
config = Object.assign({}, config)
|
||||
}
|
||||
|
||||
if (!config.plugins) {
|
||||
config.plugins = []
|
||||
}
|
||||
|
||||
return {
|
||||
file,
|
||||
plugins: loadPlugins(config, file),
|
||||
options: loadOptions(config, file),
|
||||
}
|
||||
})()
|
||||
: await postcssrc()
|
||||
|
||||
let configPlugins = config.plugins
|
||||
|
||||
let configPluginTailwindIdx = configPlugins.findIndex((plugin) => {
|
||||
if (typeof plugin === 'function' && plugin.name === 'tailwindcss') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (typeof plugin === 'object' && plugin !== null && plugin.postcssPlugin === 'tailwindcss') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
let beforePlugins =
|
||||
configPluginTailwindIdx === -1 ? [] : configPlugins.slice(0, configPluginTailwindIdx)
|
||||
let afterPlugins =
|
||||
configPluginTailwindIdx === -1
|
||||
? configPlugins
|
||||
: configPlugins.slice(configPluginTailwindIdx + 1)
|
||||
|
||||
return [beforePlugins, afterPlugins, config.options]
|
||||
}
|
||||
|
||||
function loadBuiltinPostcssPlugins() {
|
||||
let postcss = loadPostcss()
|
||||
let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
|
||||
return [
|
||||
[
|
||||
(root) => {
|
||||
root.walkAtRules('import', (rule) => {
|
||||
if (rule.params.slice(1).startsWith('tailwindcss/')) {
|
||||
rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
|
||||
rule.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
loadPostcssImport(),
|
||||
(root) => {
|
||||
root.walkComments((rule) => {
|
||||
if (rule.text.startsWith(IMPORT_COMMENT)) {
|
||||
rule.after(
|
||||
postcss.atRule({
|
||||
name: 'import',
|
||||
params: rule.text.replace(IMPORT_COMMENT, ''),
|
||||
})
|
||||
)
|
||||
rule.remove()
|
||||
}
|
||||
})
|
||||
},
|
||||
],
|
||||
[],
|
||||
{},
|
||||
]
|
||||
}
|
||||
|
||||
let state = {
|
||||
/** @type {any} */
|
||||
context: null,
|
||||
|
||||
/** @type {ReturnType<typeof createWatcher> | null} */
|
||||
watcher: null,
|
||||
|
||||
/** @type {{content: string, extension: string}[]} */
|
||||
changedContent: [],
|
||||
|
||||
/** @type {{config: Config, dependencies: Set<string>, dispose: Function } | null} */
|
||||
configBag: null,
|
||||
|
||||
contextDependencies: new Set(),
|
||||
|
||||
/** @type {import('../../lib/content.js').ContentPath[]} */
|
||||
contentPaths: [],
|
||||
|
||||
refreshContentPaths() {
|
||||
this.contentPaths = parseCandidateFiles(this.context, this.context?.tailwindConfig)
|
||||
},
|
||||
|
||||
get config() {
|
||||
return this.context.tailwindConfig
|
||||
},
|
||||
|
||||
get contentPatterns() {
|
||||
return {
|
||||
all: this.contentPaths.map((contentPath) => contentPath.pattern),
|
||||
dynamic: this.contentPaths
|
||||
.filter((contentPath) => contentPath.glob !== undefined)
|
||||
.map((contentPath) => contentPath.pattern),
|
||||
}
|
||||
},
|
||||
|
||||
loadConfig(configPath, content) {
|
||||
if (this.watcher && configPath) {
|
||||
this.refreshConfigDependencies()
|
||||
}
|
||||
|
||||
let config = loadConfig(configPath)
|
||||
let dependencies = getModuleDependencies(configPath)
|
||||
this.configBag = {
|
||||
config,
|
||||
dependencies,
|
||||
dispose() {
|
||||
for (let file of dependencies) {
|
||||
delete require.cache[require.resolve(file)]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.configBag.config = resolveConfig(this.configBag.config, { content: { files: [] } })
|
||||
|
||||
// Override content files if `--content` has been passed explicitly
|
||||
if (content?.length > 0) {
|
||||
this.configBag.config.content.files = content
|
||||
}
|
||||
|
||||
return this.configBag.config
|
||||
},
|
||||
|
||||
refreshConfigDependencies(configPath) {
|
||||
env.DEBUG && console.time('Module dependencies')
|
||||
this.configBag?.dispose()
|
||||
env.DEBUG && console.timeEnd('Module dependencies')
|
||||
},
|
||||
|
||||
readContentPaths() {
|
||||
let content = []
|
||||
|
||||
// Resolve globs from the content config
|
||||
// TODO: When we make the postcss plugin async-capable this can become async
|
||||
let files = fastGlob.sync(this.contentPatterns.all)
|
||||
|
||||
for (let file of files) {
|
||||
if (__OXIDE__) {
|
||||
content.push({
|
||||
file,
|
||||
extension: path.extname(file).slice(1),
|
||||
})
|
||||
} else {
|
||||
content.push({
|
||||
content: fs.readFileSync(path.resolve(file), 'utf8'),
|
||||
extension: path.extname(file).slice(1),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve raw content in the tailwind config
|
||||
let rawContent = this.config.content.files.filter((file) => {
|
||||
return file !== null && typeof file === 'object'
|
||||
})
|
||||
|
||||
for (let { raw: htmlContent, extension = 'html' } of rawContent) {
|
||||
content.push({ content: htmlContent, extension })
|
||||
}
|
||||
|
||||
return content
|
||||
},
|
||||
|
||||
getContext({ createContext, cliConfigPath, root, result, content }) {
|
||||
if (this.context) {
|
||||
this.context.changedContent = this.changedContent.splice(0)
|
||||
|
||||
return this.context
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('Searching for config')
|
||||
let configPath = findAtConfigPath(root, result) ?? cliConfigPath
|
||||
env.DEBUG && console.timeEnd('Searching for config')
|
||||
|
||||
env.DEBUG && console.time('Loading config')
|
||||
let config = this.loadConfig(configPath, content)
|
||||
env.DEBUG && console.timeEnd('Loading config')
|
||||
|
||||
env.DEBUG && console.time('Creating context')
|
||||
this.context = createContext(config, [])
|
||||
Object.assign(this.context, {
|
||||
userConfigPath: configPath,
|
||||
})
|
||||
env.DEBUG && console.timeEnd('Creating context')
|
||||
|
||||
env.DEBUG && console.time('Resolving content paths')
|
||||
this.refreshContentPaths()
|
||||
env.DEBUG && console.timeEnd('Resolving content paths')
|
||||
|
||||
if (this.watcher) {
|
||||
env.DEBUG && console.time('Watch new files')
|
||||
this.watcher.refreshWatchedFiles()
|
||||
env.DEBUG && console.timeEnd('Watch new files')
|
||||
}
|
||||
|
||||
for (let file of this.readContentPaths()) {
|
||||
this.context.changedContent.push(file)
|
||||
}
|
||||
|
||||
return this.context
|
||||
},
|
||||
}
|
||||
|
||||
export async function createProcessor(args, cliConfigPath) {
|
||||
let postcss = loadPostcss()
|
||||
|
||||
let input = args['--input']
|
||||
let output = args['--output']
|
||||
let includePostCss = args['--postcss']
|
||||
let customPostCssPath = typeof args['--postcss'] === 'string' ? args['--postcss'] : undefined
|
||||
|
||||
let [beforePlugins, afterPlugins, postcssOptions] = includePostCss
|
||||
? await loadPostCssPlugins(customPostCssPath)
|
||||
: loadBuiltinPostcssPlugins()
|
||||
|
||||
if (args['--purge']) {
|
||||
log.warn('purge-flag-deprecated', [
|
||||
'The `--purge` flag has been deprecated.',
|
||||
'Please use `--content` instead.',
|
||||
])
|
||||
|
||||
if (!args['--content']) {
|
||||
args['--content'] = args['--purge']
|
||||
}
|
||||
}
|
||||
|
||||
let content = args['--content']?.split(/(?<!{[^}]+),/) ?? []
|
||||
|
||||
let tailwindPlugin = () => {
|
||||
return {
|
||||
postcssPlugin: 'tailwindcss',
|
||||
Once(root, { result }) {
|
||||
env.DEBUG && console.time('Compiling CSS')
|
||||
tailwind(({ createContext }) => {
|
||||
console.error()
|
||||
console.error('Rebuilding...')
|
||||
|
||||
return () => {
|
||||
return state.getContext({
|
||||
createContext,
|
||||
cliConfigPath,
|
||||
root,
|
||||
result,
|
||||
content,
|
||||
})
|
||||
}
|
||||
})(root, result)
|
||||
env.DEBUG && console.timeEnd('Compiling CSS')
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tailwindPlugin.postcss = true
|
||||
|
||||
let plugins = [
|
||||
...beforePlugins,
|
||||
tailwindPlugin,
|
||||
!args['--minify'] && formatNodes,
|
||||
...afterPlugins,
|
||||
].filter(Boolean)
|
||||
|
||||
/** @type {import('postcss').Processor} */
|
||||
// @ts-ignore
|
||||
let processor = postcss(plugins)
|
||||
|
||||
async function readInput() {
|
||||
// Piping in data, let's drain the stdin
|
||||
if (input === '-') {
|
||||
return drainStdin()
|
||||
}
|
||||
|
||||
// Input file has been provided
|
||||
if (input) {
|
||||
return fs.promises.readFile(path.resolve(input), 'utf8')
|
||||
}
|
||||
|
||||
// No input file provided, fallback to default atrules
|
||||
return '@tailwind base; @tailwind components; @tailwind utilities'
|
||||
}
|
||||
|
||||
async function build() {
|
||||
let start = process.hrtime.bigint()
|
||||
|
||||
return readInput()
|
||||
.then((css) => processor.process(css, { ...postcssOptions, from: input, to: output }))
|
||||
.then((result) => lightningcss(!!args['--minify'], result))
|
||||
.then((result) => {
|
||||
if (!state.watcher) {
|
||||
return result
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('Recording PostCSS dependencies')
|
||||
for (let message of result.messages) {
|
||||
if (message.type === 'dependency') {
|
||||
state.contextDependencies.add(message.file)
|
||||
}
|
||||
}
|
||||
env.DEBUG && console.timeEnd('Recording PostCSS dependencies')
|
||||
|
||||
// TODO: This needs to be in a different spot
|
||||
env.DEBUG && console.time('Watch new files')
|
||||
state.watcher.refreshWatchedFiles()
|
||||
env.DEBUG && console.timeEnd('Watch new files')
|
||||
|
||||
return result
|
||||
})
|
||||
.then((result) => {
|
||||
if (!output) {
|
||||
process.stdout.write(result.css)
|
||||
return
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
outputFile(result.opts.to, result.css),
|
||||
result.map && outputFile(result.opts.to + '.map', result.map.toString()),
|
||||
])
|
||||
})
|
||||
.then(() => {
|
||||
let end = process.hrtime.bigint()
|
||||
console.error()
|
||||
console.error('Done in', (end - start) / BigInt(1e6) + 'ms.')
|
||||
})
|
||||
.then(
|
||||
() => {},
|
||||
(err) => {
|
||||
// TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
|
||||
// that were collected before the error occurred
|
||||
// The result is not stored on the error so we have to store it externally
|
||||
// and pull the messages off of it here somehow
|
||||
|
||||
// This results in a less than ideal DX because the watcher will not pick up
|
||||
// changes to imported CSS if one of them caused an error during the initial build
|
||||
// If you fix it and then save the main CSS file so there's no error
|
||||
// The watcher will start watching the imported CSS files and will be
|
||||
// resilient to future errors.
|
||||
|
||||
if (state.watcher) {
|
||||
console.error(err)
|
||||
} else {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{file: string, content(): Promise<string>, extension: string}[]} changes
|
||||
*/
|
||||
async function parseChanges(changes) {
|
||||
return Promise.all(
|
||||
changes.map(async (change) => ({
|
||||
content: await change.content(),
|
||||
extension: change.extension,
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
if (input !== undefined && input !== '-') {
|
||||
state.contextDependencies.add(path.resolve(input))
|
||||
}
|
||||
|
||||
return {
|
||||
build,
|
||||
watch: async () => {
|
||||
state.watcher = createWatcher(args, {
|
||||
state,
|
||||
|
||||
/**
|
||||
* @param {{file: string, content(): Promise<string>, extension: string}[]} changes
|
||||
*/
|
||||
async rebuild(changes) {
|
||||
let needsNewContext = changes.some((change) => {
|
||||
return (
|
||||
state.configBag?.dependencies.has(change.file) ||
|
||||
state.contextDependencies.has(change.file)
|
||||
)
|
||||
})
|
||||
|
||||
if (needsNewContext) {
|
||||
state.context = null
|
||||
} else {
|
||||
for (let change of await parseChanges(changes)) {
|
||||
state.changedContent.push(change)
|
||||
}
|
||||
}
|
||||
|
||||
return build()
|
||||
},
|
||||
})
|
||||
|
||||
await build()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export function indentRecursive(node, indent = 0) {
|
||||
node.each &&
|
||||
node.each((child, i) => {
|
||||
if (!child.raws.before || !child.raws.before.trim() || child.raws.before.includes('\n')) {
|
||||
child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${' '.repeat(indent)}`
|
||||
}
|
||||
child.raws.after = `\n${' '.repeat(indent)}`
|
||||
indentRecursive(child, indent + 1)
|
||||
})
|
||||
}
|
||||
|
||||
export function formatNodes(root) {
|
||||
indentRecursive(root)
|
||||
if (root.first) {
|
||||
root.first.raws.before = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When rapidly saving files atomically a couple of situations can happen:
|
||||
* - The file is missing since the external program has deleted it by the time we've gotten around to reading it from the earlier save.
|
||||
* - The file is being written to by the external program by the time we're going to read it and is thus treated as busy because a lock is held.
|
||||
*
|
||||
* To work around this we retry reading the file a handful of times with a delay between each attempt
|
||||
*
|
||||
* @param {string} path
|
||||
* @param {number} tries
|
||||
* @returns {Promise<string | undefined>}
|
||||
* @throws {Error} If the file is still missing or busy after the specified number of tries
|
||||
*/
|
||||
export async function readFileWithRetries(path, tries = 5) {
|
||||
for (let n = 0; n <= tries; n++) {
|
||||
try {
|
||||
return await fs.promises.readFile(path, 'utf8')
|
||||
} catch (err) {
|
||||
if (n !== tries) {
|
||||
if (err.code === 'ENOENT' || err.code === 'EBUSY') {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function drainStdin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let result = ''
|
||||
process.stdin.on('data', (chunk) => {
|
||||
result += chunk
|
||||
})
|
||||
process.stdin.on('end', () => resolve(result))
|
||||
process.stdin.on('error', (err) => reject(err))
|
||||
})
|
||||
}
|
||||
|
||||
export async function outputFile(file, newContents) {
|
||||
try {
|
||||
let currentContents = await fs.promises.readFile(file, 'utf8')
|
||||
if (currentContents === newContents) {
|
||||
return // Skip writing the file
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Write the file
|
||||
await fs.promises.mkdir(path.dirname(file), { recursive: true })
|
||||
await fs.promises.writeFile(file, newContents, 'utf8')
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue