Improve error handling
This commit is contained in:
parent
ee14d81e8e
commit
4114914eea
@ -2,5 +2,5 @@
|
|||||||
const { extendConfig } = require("@aet/eslint-rules");
|
const { extendConfig } = require("@aet/eslint-rules");
|
||||||
|
|
||||||
module.exports = extendConfig({
|
module.exports = extendConfig({
|
||||||
plugins: ["react"],
|
plugins: ["react", "unicorn"],
|
||||||
});
|
});
|
||||||
|
@ -12,13 +12,16 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aet/eslint-rules": "^0.0.21",
|
"@aet/eslint-rules": "^0.0.22",
|
||||||
"@types/babel__core": "^7.20.5",
|
"@types/babel__core": "^7.20.5",
|
||||||
"@types/bun": "^1.0.12",
|
"@types/bun": "^1.0.12",
|
||||||
|
"@types/dedent": "^0.7.2",
|
||||||
"@types/lodash": "^4.17.0",
|
"@types/lodash": "^4.17.0",
|
||||||
"@types/node": "^20.12.5",
|
"@types/node": "^20.12.5",
|
||||||
|
"dedent": "^1.5.1",
|
||||||
"esbuild": "^0.20.2",
|
"esbuild": "^0.20.2",
|
||||||
"esbuild-register": "^3.5.0",
|
"esbuild-register": "^3.5.0",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"tsup": "^8.0.2",
|
"tsup": "^8.0.2",
|
||||||
@ -43,4 +46,4 @@
|
|||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"trailingComma": "es5"
|
"trailingComma": "es5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
142
pnpm-lock.yaml
generated
142
pnpm-lock.yaml
generated
@ -20,26 +20,35 @@ dependencies:
|
|||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@aet/eslint-rules':
|
'@aet/eslint-rules':
|
||||||
specifier: ^0.0.19
|
specifier: ^0.0.22
|
||||||
version: 0.0.19(eslint@8.57.0)(typescript@5.4.4)
|
version: 0.0.22(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@types/babel__core':
|
'@types/babel__core':
|
||||||
specifier: ^7.20.5
|
specifier: ^7.20.5
|
||||||
version: 7.20.5
|
version: 7.20.5
|
||||||
'@types/bun':
|
'@types/bun':
|
||||||
specifier: ^1.0.12
|
specifier: ^1.0.12
|
||||||
version: 1.0.12
|
version: 1.0.12
|
||||||
|
'@types/dedent':
|
||||||
|
specifier: ^0.7.2
|
||||||
|
version: 0.7.2
|
||||||
'@types/lodash':
|
'@types/lodash':
|
||||||
specifier: ^4.17.0
|
specifier: ^4.17.0
|
||||||
version: 4.17.0
|
version: 4.17.0
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.12.5
|
specifier: ^20.12.5
|
||||||
version: 20.12.5
|
version: 20.12.5
|
||||||
|
dedent:
|
||||||
|
specifier: ^1.5.1
|
||||||
|
version: 1.5.1
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.20.2
|
specifier: ^0.20.2
|
||||||
version: 0.20.2
|
version: 0.20.2
|
||||||
esbuild-register:
|
esbuild-register:
|
||||||
specifier: ^3.5.0
|
specifier: ^3.5.0
|
||||||
version: 3.5.0(esbuild@0.20.2)
|
version: 3.5.0(esbuild@0.20.2)
|
||||||
|
eslint:
|
||||||
|
specifier: ^8.57.0
|
||||||
|
version: 8.57.0
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.5
|
version: 3.2.5
|
||||||
@ -66,18 +75,18 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@aet/eslint-rules@0.0.19(eslint@8.57.0)(typescript@5.4.4):
|
/@aet/eslint-rules@0.0.22(eslint@8.57.0)(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-RO9JBZcdY2HVvWPvqlob2yNwszXwOPUL81uXCg3IrjDi7Ka48zWsfEyMpF/w/3jNgwhYxDBLTJAhHABuJ2LtXQ==}
|
resolution: {integrity: sha512-PLAnaYlb6GdG6gKjQP5B8XckOa7driZL1rbw51wGTYTl/0Etqu5R/u4TkQSYePZab7xCIrJuPSC3PsBEhI5pvQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.53.0
|
eslint: ^8.57.0
|
||||||
typescript: ^5.2.2
|
typescript: ^5.4.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
|
||||||
'@types/eslint': 8.56.6
|
'@types/eslint': 8.56.7
|
||||||
'@typescript-eslint/eslint-plugin': 7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/eslint-plugin': 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
aria-query: 5.3.0
|
aria-query: 5.3.0
|
||||||
axe-core: 4.9.0
|
axe-core: 4.9.0
|
||||||
axobject-query: 4.0.0
|
axobject-query: 4.0.0
|
||||||
@ -90,10 +99,10 @@ packages:
|
|||||||
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
||||||
eslint-define-config: 1.24.1
|
eslint-define-config: 1.24.1
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.4.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
|
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
|
||||||
eslint-plugin-es-x: 7.6.0(eslint@8.57.0)
|
eslint-plugin-es-x: 7.6.0(eslint@8.57.0)
|
||||||
eslint-plugin-jsdoc: 48.2.2(eslint@8.57.0)
|
eslint-plugin-jsdoc: 48.2.3(eslint@8.57.0)
|
||||||
eslint-plugin-unicorn: 51.0.1(eslint@8.57.0)
|
eslint-plugin-unicorn: 52.0.0(eslint@8.57.0)
|
||||||
estraverse: 5.3.0
|
estraverse: 5.3.0
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
get-tsconfig: 4.7.3
|
get-tsconfig: 4.7.3
|
||||||
@ -789,7 +798,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@humanwhocodes/object-schema': 2.0.2
|
'@humanwhocodes/object-schema': 2.0.3
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -801,8 +810,8 @@ packages:
|
|||||||
engines: {node: '>=12.22'}
|
engines: {node: '>=12.22'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@humanwhocodes/object-schema@2.0.2:
|
/@humanwhocodes/object-schema@2.0.3:
|
||||||
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@isaacs/cliui@8.0.2:
|
/@isaacs/cliui@8.0.2:
|
||||||
@ -1036,8 +1045,12 @@ packages:
|
|||||||
bun-types: 1.0.36
|
bun-types: 1.0.36
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/eslint@8.56.6:
|
/@types/dedent@0.7.2:
|
||||||
resolution: {integrity: sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==}
|
resolution: {integrity: sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/eslint@8.56.7:
|
||||||
|
resolution: {integrity: sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.5
|
'@types/estree': 1.0.5
|
||||||
'@types/json-schema': 7.0.15
|
'@types/json-schema': 7.0.15
|
||||||
@ -1081,8 +1094,8 @@ packages:
|
|||||||
'@types/node': 20.12.5
|
'@types/node': 20.12.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin@7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.4):
|
/@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==}
|
resolution: {integrity: sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@typescript-eslint/parser': ^7.0.0
|
'@typescript-eslint/parser': ^7.0.0
|
||||||
@ -1093,11 +1106,11 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.10.0
|
'@eslint-community/regexpp': 4.10.0
|
||||||
'@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/scope-manager': 7.4.0
|
'@typescript-eslint/scope-manager': 7.5.0
|
||||||
'@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
'@typescript-eslint/visitor-keys': 7.4.0
|
'@typescript-eslint/visitor-keys': 7.5.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
graphemer: 1.4.0
|
graphemer: 1.4.0
|
||||||
@ -1110,8 +1123,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/parser@7.4.0(eslint@8.57.0)(typescript@5.4.4):
|
/@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==}
|
resolution: {integrity: sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.56.0
|
eslint: ^8.56.0
|
||||||
@ -1120,10 +1133,10 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 7.4.0
|
'@typescript-eslint/scope-manager': 7.5.0
|
||||||
'@typescript-eslint/types': 7.4.0
|
'@typescript-eslint/types': 7.5.0
|
||||||
'@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.4)
|
'@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4)
|
||||||
'@typescript-eslint/visitor-keys': 7.4.0
|
'@typescript-eslint/visitor-keys': 7.5.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
typescript: 5.4.4
|
typescript: 5.4.4
|
||||||
@ -1131,16 +1144,16 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/scope-manager@7.4.0:
|
/@typescript-eslint/scope-manager@7.5.0:
|
||||||
resolution: {integrity: sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==}
|
resolution: {integrity: sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 7.4.0
|
'@typescript-eslint/types': 7.5.0
|
||||||
'@typescript-eslint/visitor-keys': 7.4.0
|
'@typescript-eslint/visitor-keys': 7.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/type-utils@7.4.0(eslint@8.57.0)(typescript@5.4.4):
|
/@typescript-eslint/type-utils@7.5.0(eslint@8.57.0)(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==}
|
resolution: {integrity: sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.56.0
|
eslint: ^8.56.0
|
||||||
@ -1149,8 +1162,8 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.4)
|
'@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4)
|
||||||
'@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
ts-api-utils: 1.3.0(typescript@5.4.4)
|
ts-api-utils: 1.3.0(typescript@5.4.4)
|
||||||
@ -1159,13 +1172,13 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/types@7.4.0:
|
/@typescript-eslint/types@7.5.0:
|
||||||
resolution: {integrity: sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==}
|
resolution: {integrity: sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/typescript-estree@7.4.0(typescript@5.4.4):
|
/@typescript-eslint/typescript-estree@7.5.0(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==}
|
resolution: {integrity: sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '*'
|
typescript: '*'
|
||||||
@ -1173,8 +1186,8 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 7.4.0
|
'@typescript-eslint/types': 7.5.0
|
||||||
'@typescript-eslint/visitor-keys': 7.4.0
|
'@typescript-eslint/visitor-keys': 7.5.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -1186,8 +1199,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/utils@7.4.0(eslint@8.57.0)(typescript@5.4.4):
|
/@typescript-eslint/utils@7.5.0(eslint@8.57.0)(typescript@5.4.4):
|
||||||
resolution: {integrity: sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==}
|
resolution: {integrity: sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^8.56.0
|
eslint: ^8.56.0
|
||||||
@ -1195,9 +1208,9 @@ packages:
|
|||||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
|
||||||
'@types/json-schema': 7.0.15
|
'@types/json-schema': 7.0.15
|
||||||
'@types/semver': 7.5.8
|
'@types/semver': 7.5.8
|
||||||
'@typescript-eslint/scope-manager': 7.4.0
|
'@typescript-eslint/scope-manager': 7.5.0
|
||||||
'@typescript-eslint/types': 7.4.0
|
'@typescript-eslint/types': 7.5.0
|
||||||
'@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.4)
|
'@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4)
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
semver: 7.6.0
|
semver: 7.6.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -1205,11 +1218,11 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/visitor-keys@7.4.0:
|
/@typescript-eslint/visitor-keys@7.5.0:
|
||||||
resolution: {integrity: sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==}
|
resolution: {integrity: sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==}
|
||||||
engines: {node: ^18.18.0 || >=20.0.0}
|
engines: {node: ^18.18.0 || >=20.0.0}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 7.4.0
|
'@typescript-eslint/types': 7.5.0
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -1593,6 +1606,15 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
|
|
||||||
|
/dedent@1.5.1:
|
||||||
|
resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
|
||||||
|
peerDependencies:
|
||||||
|
babel-plugin-macros: ^3.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
babel-plugin-macros:
|
||||||
|
optional: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/deep-eql@4.1.3:
|
/deep-eql@4.1.3:
|
||||||
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
|
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -1789,7 +1811,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.4.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
|
/eslint-module-utils@2.8.1(@typescript-eslint/parser@7.5.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
|
||||||
resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
|
resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1810,7 +1832,7 @@ packages:
|
|||||||
eslint-import-resolver-webpack:
|
eslint-import-resolver-webpack:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.4)
|
'@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.4)
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
@ -1830,8 +1852,8 @@ packages:
|
|||||||
eslint-compat-utils: 0.5.0(eslint@8.57.0)
|
eslint-compat-utils: 0.5.0(eslint@8.57.0)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-jsdoc@48.2.2(eslint@8.57.0):
|
/eslint-plugin-jsdoc@48.2.3(eslint@8.57.0):
|
||||||
resolution: {integrity: sha512-S0Gk+rpT5w/ephKCncUY7kUsix9uE4B9XI8D/fS1/26d8okE+vZsuG1IvIt4B6sJUdQqsnzi+YXfmh+HJG11CA==}
|
resolution: {integrity: sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
|
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0
|
||||||
@ -1850,8 +1872,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-plugin-unicorn@51.0.1(eslint@8.57.0):
|
/eslint-plugin-unicorn@52.0.0(eslint@8.57.0):
|
||||||
resolution: {integrity: sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==}
|
resolution: {integrity: sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=8.56.0'
|
eslint: '>=8.56.0'
|
||||||
|
211
src/babel-tailwind.ts
Normal file
211
src/babel-tailwind.ts
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import { basename, dirname, extname, join } from "node:path";
|
||||||
|
import type babel from "@babel/core";
|
||||||
|
import { type NodePath, type types as t } from "@babel/core";
|
||||||
|
import type { SourceLocation, StyleMap, StyleMapEntry } from "./shared";
|
||||||
|
import { type TailwindPluginOptions, getClassName } from "./index";
|
||||||
|
|
||||||
|
interface BabelPluginState {
|
||||||
|
getCx: () => t.Identifier;
|
||||||
|
sliceText: (node: t.Node) => SourceLocation;
|
||||||
|
tailwindMap: Map<string, StyleMapEntry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const definePlugin =
|
||||||
|
<T>(fn: (runtime: typeof babel) => babel.Visitor<babel.PluginPass & T>) =>
|
||||||
|
(runtime: typeof babel) => {
|
||||||
|
const plugin: babel.PluginObj<babel.PluginPass & T> = {
|
||||||
|
visitor: fn(runtime),
|
||||||
|
};
|
||||||
|
return plugin as babel.PluginObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractJSXContainer = (attr: NonNullable<t.JSXAttribute["value"]>): t.Expression =>
|
||||||
|
attr.type === "JSXExpressionContainer" ? (attr.expression as t.Expression) : attr;
|
||||||
|
|
||||||
|
function matchPath(
|
||||||
|
nodePath: NodePath<t.Node | null | undefined>,
|
||||||
|
fns: (dig: (nodePath: NodePath) => void) => babel.Visitor
|
||||||
|
) {
|
||||||
|
if (!nodePath.node) return;
|
||||||
|
const fn = fns(path => matchPath(path, fns))[nodePath.node.type] as any;
|
||||||
|
fn?.(nodePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function babelTailwind(
|
||||||
|
styleMap: StyleMap,
|
||||||
|
{
|
||||||
|
clsx,
|
||||||
|
getClassName: getClass = getClassName,
|
||||||
|
taggedTemplateName,
|
||||||
|
jsxAttributeAction = "delete",
|
||||||
|
jsxAttributeName = "css",
|
||||||
|
}: TailwindPluginOptions
|
||||||
|
) {
|
||||||
|
function getClsxImport(t: typeof babel.types, cx: t.Identifier) {
|
||||||
|
switch (clsx) {
|
||||||
|
case "emotion":
|
||||||
|
return t.importDeclaration(
|
||||||
|
[t.importSpecifier(cx, t.identifier("cx"))],
|
||||||
|
t.stringLiteral("@emotion/css")
|
||||||
|
);
|
||||||
|
case "clsx":
|
||||||
|
return t.importDeclaration(
|
||||||
|
[t.importDefaultSpecifier(cx)],
|
||||||
|
t.stringLiteral("clsx")
|
||||||
|
);
|
||||||
|
case "classnames":
|
||||||
|
return t.importDeclaration(
|
||||||
|
[t.importDefaultSpecifier(cx)],
|
||||||
|
t.stringLiteral("classnames")
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown clsx library");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return definePlugin<BabelPluginState>(({ types: t }) => ({
|
||||||
|
Program: {
|
||||||
|
enter(path, state) {
|
||||||
|
let cx: t.Identifier;
|
||||||
|
state.tailwindMap = new Map();
|
||||||
|
state.sliceText = node => ({
|
||||||
|
filename: state.filename!,
|
||||||
|
start: node.loc!.start,
|
||||||
|
end: node.loc!.end,
|
||||||
|
text: state.file.code
|
||||||
|
.split("\n")
|
||||||
|
.slice(node.loc!.start.line - 1, node.loc!.end.line)
|
||||||
|
.join("\n"),
|
||||||
|
});
|
||||||
|
state.getCx = () => {
|
||||||
|
if (cx == null) {
|
||||||
|
cx = path.scope.generateUidIdentifier("cx");
|
||||||
|
path.node.body.unshift(getClsxImport(t, cx));
|
||||||
|
}
|
||||||
|
return t.cloneNode(cx);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
exit({ node }, { filename, tailwindMap }) {
|
||||||
|
if (!tailwindMap.size) return;
|
||||||
|
if (!filename) {
|
||||||
|
throw new Error("babel: missing state.filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
const cssName = basename(filename, extname(filename)) + ".css";
|
||||||
|
node.body.unshift(
|
||||||
|
t.importDeclaration([], t.stringLiteral(`tailwind:./${cssName}`))
|
||||||
|
);
|
||||||
|
|
||||||
|
styleMap.set(join(dirname(filename), cssName), Array.from(tailwindMap.values()));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
TaggedTemplateExpression(path, { tailwindMap, sliceText }) {
|
||||||
|
if (taggedTemplateName == null) return;
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
const {
|
||||||
|
tag,
|
||||||
|
quasi: { quasis, expressions },
|
||||||
|
} = node;
|
||||||
|
if (!t.isIdentifier(tag, { name: taggedTemplateName })) return;
|
||||||
|
|
||||||
|
if (expressions.length) {
|
||||||
|
throw new Error(`${taggedTemplateName}\`\` should not contain expressions`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = quasis[0].value.cooked;
|
||||||
|
if (value) {
|
||||||
|
const trimmed = value.replace(/\s+/g, " ").trim();
|
||||||
|
const className = getClass(trimmed);
|
||||||
|
tailwindMap.set(className, {
|
||||||
|
key: className,
|
||||||
|
className: trimmed,
|
||||||
|
location: sliceText(node),
|
||||||
|
});
|
||||||
|
path.replaceWith(t.stringLiteral(className));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
JSXAttribute(path, { tailwindMap, sliceText, getCx }) {
|
||||||
|
const { name } = path.node;
|
||||||
|
if (name.name !== jsxAttributeName) return;
|
||||||
|
|
||||||
|
const valuePath = path.get("value");
|
||||||
|
if (!valuePath.node) return;
|
||||||
|
|
||||||
|
const copy =
|
||||||
|
jsxAttributeAction === "delete" ? undefined : t.cloneNode(valuePath.node, true);
|
||||||
|
|
||||||
|
const parent = path.parent as t.JSXOpeningElement;
|
||||||
|
const classNameAttribute = parent.attributes.find(
|
||||||
|
(attr): attr is t.JSXAttribute =>
|
||||||
|
t.isJSXAttribute(attr) && attr.name.name === "className"
|
||||||
|
);
|
||||||
|
|
||||||
|
matchPath(valuePath, go => ({
|
||||||
|
StringLiteral(path) {
|
||||||
|
const { node } = path;
|
||||||
|
const { value } = node;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
const trimmed = value.replace(/\s+/g, " ").trim();
|
||||||
|
const className = getClass(trimmed);
|
||||||
|
tailwindMap.set(className, {
|
||||||
|
key: className,
|
||||||
|
className: trimmed,
|
||||||
|
location: sliceText(node),
|
||||||
|
});
|
||||||
|
path.replaceWith(t.stringLiteral(className));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JSXExpressionContainer(path) {
|
||||||
|
go(path.get("expression"));
|
||||||
|
},
|
||||||
|
ConditionalExpression(path) {
|
||||||
|
go(path.get("consequent"));
|
||||||
|
go(path.get("alternate"));
|
||||||
|
},
|
||||||
|
LogicalExpression(path) {
|
||||||
|
go(path.get("right"));
|
||||||
|
},
|
||||||
|
CallExpression(path) {
|
||||||
|
for (const arg of path.get("arguments")) {
|
||||||
|
go(arg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (classNameAttribute) {
|
||||||
|
const attrValue = classNameAttribute.value!;
|
||||||
|
|
||||||
|
// If both are string literals, we can merge them directly here
|
||||||
|
if (t.isStringLiteral(attrValue) && t.isStringLiteral(valuePath.node)) {
|
||||||
|
attrValue.value +=
|
||||||
|
(attrValue.value.at(-1) === " " ? "" : " ") + valuePath.node.value;
|
||||||
|
} else {
|
||||||
|
classNameAttribute.value = t.jsxExpressionContainer(
|
||||||
|
t.callExpression(getCx(), [
|
||||||
|
extractJSXContainer(attrValue),
|
||||||
|
extractJSXContainer(valuePath.node),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent.attributes.push(
|
||||||
|
t.jsxAttribute(t.jsxIdentifier("className"), valuePath.node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsxAttributeAction === "delete") {
|
||||||
|
path.remove();
|
||||||
|
} else {
|
||||||
|
path.node.value = copy!;
|
||||||
|
if (Array.isArray(jsxAttributeAction) && jsxAttributeAction[0] === "rename") {
|
||||||
|
path.node.name.name = jsxAttributeAction[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
90
src/esbuild-postcss.ts
Normal file
90
src/esbuild-postcss.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import type * as esbuild from "esbuild";
|
||||||
|
import { CssSyntaxError } from "postcss";
|
||||||
|
import { type Compile, type StyleMap, pkgName, toCSSText } from "./shared";
|
||||||
|
|
||||||
|
const PLUGIN_NAME = "tailwind";
|
||||||
|
const ESBUILD_NAMESPACE = "babel-tailwind";
|
||||||
|
|
||||||
|
export const esbuildPlugin = (styleMap: StyleMap, compile: Compile): esbuild.Plugin => ({
|
||||||
|
name: PLUGIN_NAME,
|
||||||
|
|
||||||
|
setup(build) {
|
||||||
|
build.onResolve({ filter: /^tailwind:.+\.css$/ }, ({ path, importer }) => {
|
||||||
|
const resolved = join(dirname(importer), path.replace(/^tailwind:/, ""));
|
||||||
|
if (styleMap.has(resolved)) {
|
||||||
|
return {
|
||||||
|
path: resolved,
|
||||||
|
namespace: ESBUILD_NAMESPACE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
build.onResolve({ filter: RegExp(`^${pkgName}/base$`) }, () => ({
|
||||||
|
path: "directive:base",
|
||||||
|
namespace: ESBUILD_NAMESPACE,
|
||||||
|
}));
|
||||||
|
|
||||||
|
build.onLoad({ filter: /.*/, namespace: ESBUILD_NAMESPACE }, async ({ path }) => {
|
||||||
|
if (path === "directive:base") {
|
||||||
|
return {
|
||||||
|
contents: (await compile("@tailwind base")).css,
|
||||||
|
loader: "css",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!styleMap.has(path)) return;
|
||||||
|
|
||||||
|
const styles = styleMap.get(path)!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await compile(toCSSText(styles));
|
||||||
|
return {
|
||||||
|
contents: result.css,
|
||||||
|
loader: "css",
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof CssSyntaxError) {
|
||||||
|
const lines = e.source!.split("\n");
|
||||||
|
const cls = lines
|
||||||
|
.at(e.line! - 2)!
|
||||||
|
.slice(1, -1)
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const entry = styles.find(s => s.key === cls)!;
|
||||||
|
if (!entry) {
|
||||||
|
throw new Error("Could not find entry for CSS");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { location: loc } = entry;
|
||||||
|
const errLoc: Partial<esbuild.Location> = {
|
||||||
|
file: loc.filename,
|
||||||
|
line: loc.start.line,
|
||||||
|
column: loc.start.column,
|
||||||
|
length: loc.end.column - loc.start.column,
|
||||||
|
lineText: loc.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
const doesNotExist = e.reason.match(/The `(.+)` class does not exist/)?.[1];
|
||||||
|
if (doesNotExist) {
|
||||||
|
const index = loc.text.indexOf(doesNotExist, loc.start.column);
|
||||||
|
if (index !== -1) {
|
||||||
|
errLoc.column = index;
|
||||||
|
errLoc.length = doesNotExist.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
text: e.reason,
|
||||||
|
location: errLoc,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@ -1,7 +1,8 @@
|
|||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { type OutputFile, build, transformSync } from "esbuild";
|
import { type BuildOptions, type OutputFile, build, transformSync } from "esbuild";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import dedent from "dedent";
|
||||||
import { name } from "../package.json" with { type: "json" };
|
import { name } from "../package.json" with { type: "json" };
|
||||||
import {
|
import {
|
||||||
type TailwindPluginOptions,
|
type TailwindPluginOptions,
|
||||||
@ -22,44 +23,10 @@ describe("babel-tailwind", () => {
|
|||||||
await fs.rm(folder, { recursive: true, force: true });
|
await fs.rm(folder, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
async function write(path: string, content: string) {
|
|
||||||
const resolved = resolve(folder, path);
|
|
||||||
await fs.writeFile(resolved, content);
|
|
||||||
return resolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
const minCSS = (text: string) =>
|
|
||||||
transformSync(text, { minify: true, loader: "css" }).code;
|
|
||||||
|
|
||||||
const findByExt = (outputFiles: OutputFile[], ext: string) =>
|
|
||||||
outputFiles.find(file => file.path.endsWith(ext))!;
|
|
||||||
|
|
||||||
async function compileESBuild(options: TailwindPluginOptions, javascript: string) {
|
|
||||||
const tailwind = getTailwindPlugins({
|
|
||||||
tailwindConfig: {},
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
const result = await build({
|
|
||||||
bundle: true,
|
|
||||||
write: false,
|
|
||||||
external: ["react/jsx-runtime"],
|
|
||||||
outdir: "dist",
|
|
||||||
format: "esm",
|
|
||||||
entryPoints: [await write("index.tsx", javascript)],
|
|
||||||
plugins: [babelPlugin({ plugins: [tailwind.babel] }), tailwind.esbuild],
|
|
||||||
});
|
|
||||||
|
|
||||||
const { errors, warnings, outputFiles } = result;
|
|
||||||
expect(errors).toHaveLength(0);
|
|
||||||
expect(warnings).toHaveLength(0);
|
|
||||||
|
|
||||||
return outputFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
it("supports ESBuild", async () => {
|
it("supports ESBuild", async () => {
|
||||||
const outputFiles = await compileESBuild(
|
const outputFiles = await compileESBuild({
|
||||||
{ clsx: "emotion" },
|
clsx: "emotion",
|
||||||
/* tsx */ `
|
javascript: /* tsx */ `
|
||||||
export function Hello() {
|
export function Hello() {
|
||||||
return (
|
return (
|
||||||
<div css="text-center">
|
<div css="text-center">
|
||||||
@ -67,8 +34,8 @@ describe("babel-tailwind", () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
);
|
});
|
||||||
expect(outputFiles).toHaveLength(2);
|
expect(outputFiles).toHaveLength(2);
|
||||||
|
|
||||||
const js = findByExt(outputFiles, ".js");
|
const js = findByExt(outputFiles, ".js");
|
||||||
@ -80,9 +47,10 @@ describe("babel-tailwind", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not remove the attribute if `preserveAttribute` is true", async () => {
|
it("does not remove the attribute if `preserveAttribute` is true", async () => {
|
||||||
const outputFiles = await compileESBuild(
|
const outputFiles = await compileESBuild({
|
||||||
{ clsx: "emotion", jsxAttributeAction: "preserve" },
|
clsx: "emotion",
|
||||||
/* tsx */ `
|
jsxAttributeAction: "preserve",
|
||||||
|
javascript: /* tsx */ `
|
||||||
export function Hello() {
|
export function Hello() {
|
||||||
return (
|
return (
|
||||||
<div css="text-center">
|
<div css="text-center">
|
||||||
@ -90,18 +58,50 @@ describe("babel-tailwind", () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
);
|
});
|
||||||
expect(outputFiles).toHaveLength(2);
|
expect(outputFiles).toHaveLength(2);
|
||||||
|
|
||||||
const js = findByExt(outputFiles, ".js");
|
const js = findByExt(outputFiles, ".js");
|
||||||
expect(js.text).toContain(`css: "text-center"`);
|
expect(js.text).toContain(`css: "text-center"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reports errors with correct position", async () => {
|
||||||
|
try {
|
||||||
|
await compileESBuild({
|
||||||
|
clsx: "emotion",
|
||||||
|
jsxAttributeAction: "preserve",
|
||||||
|
esbuild: {
|
||||||
|
logLevel: "silent",
|
||||||
|
},
|
||||||
|
javascript: /* tsx */ `
|
||||||
|
export function Hello() {
|
||||||
|
return (
|
||||||
|
<div css="text-center2 m-0">
|
||||||
|
Hello, world!
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
throw new Error("Expected an error");
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.errors).toHaveLength(1);
|
||||||
|
const [error] = e.errors;
|
||||||
|
expect(error.location).toMatchObject({
|
||||||
|
column: 14,
|
||||||
|
length: 12,
|
||||||
|
line: 3,
|
||||||
|
lineText: ' <div css="text-center2 m-0">',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("supports custom jsxAttributeName", async () => {
|
it("supports custom jsxAttributeName", async () => {
|
||||||
const outputFiles = await compileESBuild(
|
const outputFiles = await compileESBuild({
|
||||||
{ clsx: "emotion", jsxAttributeName: "tw" },
|
clsx: "emotion",
|
||||||
/* tsx */ `
|
jsxAttributeName: "tw",
|
||||||
|
javascript: /* tsx */ `
|
||||||
export function Hello() {
|
export function Hello() {
|
||||||
return (
|
return (
|
||||||
<div tw="text-center">
|
<div tw="text-center">
|
||||||
@ -109,8 +109,8 @@ describe("babel-tailwind", () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
);
|
});
|
||||||
expect(outputFiles).toHaveLength(2);
|
expect(outputFiles).toHaveLength(2);
|
||||||
|
|
||||||
const js = findByExt(outputFiles, ".js");
|
const js = findByExt(outputFiles, ".js");
|
||||||
@ -124,12 +124,12 @@ describe("babel-tailwind", () => {
|
|||||||
it("supports importing tailwind/base", async () => {
|
it("supports importing tailwind/base", async () => {
|
||||||
const postcss = createPostCSS({ tailwindConfig: {} });
|
const postcss = createPostCSS({ tailwindConfig: {} });
|
||||||
const base = (await postcss("@tailwind base;")).css;
|
const base = (await postcss("@tailwind base;")).css;
|
||||||
const outputFiles = await compileESBuild(
|
const outputFiles = await compileESBuild({
|
||||||
{ clsx: "emotion" },
|
clsx: "emotion",
|
||||||
/* tsx */ `
|
javascript: /* tsx */ `
|
||||||
import "${name}/base";
|
import "${name}/base";
|
||||||
`
|
`,
|
||||||
);
|
});
|
||||||
expect(outputFiles).toHaveLength(2);
|
expect(outputFiles).toHaveLength(2);
|
||||||
|
|
||||||
const js = findByExt(outputFiles, ".js");
|
const js = findByExt(outputFiles, ".js");
|
||||||
@ -141,3 +141,45 @@ describe("babel-tailwind", () => {
|
|||||||
expect(minCSS(css.text)).toContain(minCSS(base));
|
expect(minCSS(css.text)).toContain(minCSS(base));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function write(path: string, content: string) {
|
||||||
|
const resolved = resolve(folder, path);
|
||||||
|
await fs.writeFile(resolved, content);
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
const minCSS = (text: string) =>
|
||||||
|
transformSync(text, { minify: true, loader: "css" }).code;
|
||||||
|
|
||||||
|
const findByExt = (outputFiles: OutputFile[], ext: string) =>
|
||||||
|
outputFiles.find(file => file.path.endsWith(ext))!;
|
||||||
|
|
||||||
|
async function compileESBuild({
|
||||||
|
javascript,
|
||||||
|
esbuild,
|
||||||
|
...options
|
||||||
|
}: TailwindPluginOptions & {
|
||||||
|
esbuild?: BuildOptions;
|
||||||
|
javascript: string;
|
||||||
|
}) {
|
||||||
|
const tailwind = getTailwindPlugins({
|
||||||
|
tailwindConfig: {},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
const result = await build({
|
||||||
|
bundle: true,
|
||||||
|
write: false,
|
||||||
|
external: ["react/jsx-runtime"],
|
||||||
|
outdir: "dist",
|
||||||
|
format: "esm",
|
||||||
|
entryPoints: [await write("index.tsx", dedent(javascript))],
|
||||||
|
plugins: [babelPlugin({ plugins: [tailwind.babel] }), tailwind.esbuild],
|
||||||
|
...esbuild,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { errors, warnings, outputFiles } = result;
|
||||||
|
expect(errors).toHaveLength(0);
|
||||||
|
expect(warnings).toHaveLength(0);
|
||||||
|
|
||||||
|
return outputFiles!;
|
||||||
|
}
|
||||||
|
315
src/index.ts
315
src/index.ts
@ -1,42 +1,15 @@
|
|||||||
import { basename, dirname, extname, join } from "node:path";
|
|
||||||
import hash from "@emotion/hash";
|
import hash from "@emotion/hash";
|
||||||
import type babel from "@babel/core";
|
import type { Config } from "tailwindcss";
|
||||||
import type * as vite from "vite";
|
import type postcss from "postcss";
|
||||||
import type * as esbuild from "esbuild";
|
import { babelTailwind } from "./babel-tailwind";
|
||||||
import { type NodePath, type types as t } from "@babel/core";
|
import { esbuildPlugin } from "./esbuild-postcss";
|
||||||
import tailwind, { type Config } from "tailwindcss";
|
import { vitePlugin } from "./vite-plugin";
|
||||||
import postcss from "postcss";
|
import { type StyleMap, createPostCSS, type tailwindDirectives } from "./shared";
|
||||||
|
|
||||||
export { babelPlugin } from "./esbuild-babel";
|
export { babelPlugin } from "./esbuild-babel";
|
||||||
|
export { createPostCSS } from "./shared";
|
||||||
|
|
||||||
const PLUGIN_NAME = "tailwind";
|
type GetClassName = (className: string) => string;
|
||||||
const ESBUILD_NAMESPACE = "babel-tailwind";
|
|
||||||
const ROLLUP_PREFIX = "\0tailwind:";
|
|
||||||
|
|
||||||
const { name } = [require][0](
|
|
||||||
process.env.BABEL_TAILWIND_BUILD ? "./package.json" : "../package.json"
|
|
||||||
);
|
|
||||||
|
|
||||||
const definePlugin =
|
|
||||||
<T>(fn: (runtime: typeof babel) => babel.Visitor<babel.PluginPass & T>) =>
|
|
||||||
(runtime: typeof babel) => {
|
|
||||||
const plugin: babel.PluginObj<babel.PluginPass & T> = {
|
|
||||||
visitor: fn(runtime),
|
|
||||||
};
|
|
||||||
return plugin as babel.PluginObj;
|
|
||||||
};
|
|
||||||
|
|
||||||
const extractJSXContainer = (attr: NonNullable<t.JSXAttribute["value"]>): t.Expression =>
|
|
||||||
attr.type === "JSXExpressionContainer" ? (attr.expression as t.Expression) : attr;
|
|
||||||
|
|
||||||
function matchPath(
|
|
||||||
nodePath: NodePath<t.Node | null | undefined>,
|
|
||||||
fns: (dig: (nodePath: NodePath) => void) => babel.Visitor
|
|
||||||
) {
|
|
||||||
if (!nodePath.node) return;
|
|
||||||
const fn = fns(path => matchPath(path, fns))[nodePath.node.type] as any;
|
|
||||||
fn?.(nodePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tagged template macro function for Tailwind classes
|
* Tagged template macro function for Tailwind classes
|
||||||
@ -44,8 +17,6 @@ function matchPath(
|
|||||||
*/
|
*/
|
||||||
export type TaggedTailwindFunction = (strings: TemplateStringsArray) => string;
|
export type TaggedTailwindFunction = (strings: TemplateStringsArray) => string;
|
||||||
|
|
||||||
const tailwindDirectives = ["components", "utilities", "variants"] as const;
|
|
||||||
|
|
||||||
export interface TailwindPluginOptions {
|
export interface TailwindPluginOptions {
|
||||||
/**
|
/**
|
||||||
* Tailwind CSS configuration
|
* Tailwind CSS configuration
|
||||||
@ -95,280 +66,12 @@ export interface TailwindPluginOptions {
|
|||||||
clsx: "clsx" | "classnames" | "emotion";
|
clsx: "clsx" | "classnames" | "emotion";
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetClassName = (className: string) => string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hashes and prefixes a string of Tailwind class names.
|
* Hashes and prefixes a string of Tailwind class names.
|
||||||
* @example getClassName("p-2 text-center") // "tw-1r6fxxz"
|
* @example getClassName("p-2 text-center") // "tw-1r6fxxz"
|
||||||
*/
|
*/
|
||||||
export const getClassName: GetClassName = cls => "tw-" + hash(cls);
|
export const getClassName: GetClassName = cls => "tw-" + hash(cls);
|
||||||
|
|
||||||
interface BabelPluginState {
|
|
||||||
getCx: () => t.Identifier;
|
|
||||||
tailwindMap: Map<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function babelTailwind(
|
|
||||||
styleMap: Map<string, string>,
|
|
||||||
{
|
|
||||||
clsx,
|
|
||||||
getClassName: getClass = getClassName,
|
|
||||||
taggedTemplateName,
|
|
||||||
jsxAttributeAction = "delete",
|
|
||||||
jsxAttributeName = "css",
|
|
||||||
}: TailwindPluginOptions
|
|
||||||
) {
|
|
||||||
function getClsxImport(t: typeof babel.types, cx: t.Identifier) {
|
|
||||||
switch (clsx) {
|
|
||||||
case "emotion":
|
|
||||||
return t.importDeclaration(
|
|
||||||
[t.importSpecifier(cx, t.identifier("cx"))],
|
|
||||||
t.stringLiteral("@emotion/css")
|
|
||||||
);
|
|
||||||
case "clsx":
|
|
||||||
return t.importDeclaration(
|
|
||||||
[t.importDefaultSpecifier(cx)],
|
|
||||||
t.stringLiteral("clsx")
|
|
||||||
);
|
|
||||||
case "classnames":
|
|
||||||
return t.importDeclaration(
|
|
||||||
[t.importDefaultSpecifier(cx)],
|
|
||||||
t.stringLiteral("classnames")
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown clsx library");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return definePlugin<BabelPluginState>(({ types: t }) => ({
|
|
||||||
Program: {
|
|
||||||
enter(path, state) {
|
|
||||||
let cx: t.Identifier;
|
|
||||||
state.tailwindMap = new Map();
|
|
||||||
state.getCx = () => {
|
|
||||||
if (cx == null) {
|
|
||||||
cx = path.scope.generateUidIdentifier("cx");
|
|
||||||
path.node.body.unshift(getClsxImport(t, cx));
|
|
||||||
}
|
|
||||||
return t.cloneNode(cx);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
exit({ node }, { filename, tailwindMap }) {
|
|
||||||
if (!tailwindMap.size) return;
|
|
||||||
if (!filename) {
|
|
||||||
throw new Error("babel: missing state.filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
const cssName = basename(filename, extname(filename)) + ".css";
|
|
||||||
node.body.unshift(
|
|
||||||
t.importDeclaration([], t.stringLiteral(`tailwind:./${cssName}`))
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = Array.from(tailwindMap)
|
|
||||||
.map(([className, value]) => `.${className} {\n @apply ${value}\n}`)
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
styleMap.set(join(dirname(filename), cssName), result);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
TaggedTemplateExpression(path, { tailwindMap }) {
|
|
||||||
if (taggedTemplateName == null) return;
|
|
||||||
|
|
||||||
const {
|
|
||||||
tag,
|
|
||||||
quasi: { quasis, expressions },
|
|
||||||
} = path.node;
|
|
||||||
if (!t.isIdentifier(tag, { name: taggedTemplateName })) return;
|
|
||||||
|
|
||||||
if (expressions.length) {
|
|
||||||
throw new Error(`${taggedTemplateName}\`\` should not contain expressions`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = quasis[0].value.cooked;
|
|
||||||
if (value) {
|
|
||||||
const trimmed = value.replace(/\s+/g, " ").trim();
|
|
||||||
const className = getClass(trimmed);
|
|
||||||
tailwindMap.set(className, trimmed);
|
|
||||||
path.replaceWith(t.stringLiteral(className));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
JSXAttribute(path, { tailwindMap, getCx }) {
|
|
||||||
const { name } = path.node;
|
|
||||||
if (name.name !== jsxAttributeName) return;
|
|
||||||
|
|
||||||
const valuePath = path.get("value");
|
|
||||||
if (!valuePath.node) return;
|
|
||||||
|
|
||||||
const copy =
|
|
||||||
jsxAttributeAction === "delete" ? undefined : t.cloneNode(valuePath.node, true);
|
|
||||||
|
|
||||||
const parent = path.parent as t.JSXOpeningElement;
|
|
||||||
const classNameAttribute = parent.attributes.find(
|
|
||||||
(attr): attr is t.JSXAttribute =>
|
|
||||||
t.isJSXAttribute(attr) && attr.name.name === "className"
|
|
||||||
);
|
|
||||||
|
|
||||||
matchPath(valuePath, go => ({
|
|
||||||
StringLiteral(path) {
|
|
||||||
const { value } = path.node;
|
|
||||||
if (value) {
|
|
||||||
const trimmed = value.replace(/\s+/g, " ").trim();
|
|
||||||
const className = getClass(trimmed);
|
|
||||||
tailwindMap.set(className, trimmed);
|
|
||||||
path.replaceWith(t.stringLiteral(className));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
JSXExpressionContainer(path) {
|
|
||||||
go(path.get("expression"));
|
|
||||||
},
|
|
||||||
ConditionalExpression(path) {
|
|
||||||
go(path.get("consequent"));
|
|
||||||
go(path.get("alternate"));
|
|
||||||
},
|
|
||||||
LogicalExpression(path) {
|
|
||||||
go(path.get("right"));
|
|
||||||
},
|
|
||||||
CallExpression(path) {
|
|
||||||
for (const arg of path.get("arguments")) {
|
|
||||||
go(arg);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (classNameAttribute) {
|
|
||||||
const attrValue = classNameAttribute.value!;
|
|
||||||
|
|
||||||
// If both are string literals, we can merge them directly here
|
|
||||||
if (t.isStringLiteral(attrValue) && t.isStringLiteral(valuePath.node)) {
|
|
||||||
attrValue.value +=
|
|
||||||
(attrValue.value.at(-1) === " " ? "" : " ") + valuePath.node.value;
|
|
||||||
} else {
|
|
||||||
classNameAttribute.value = t.jsxExpressionContainer(
|
|
||||||
t.callExpression(getCx(), [
|
|
||||||
extractJSXContainer(attrValue),
|
|
||||||
extractJSXContainer(valuePath.node),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parent.attributes.push(
|
|
||||||
t.jsxAttribute(t.jsxIdentifier("className"), valuePath.node)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsxAttributeAction === "delete") {
|
|
||||||
path.remove();
|
|
||||||
} else {
|
|
||||||
path.node.value = copy!;
|
|
||||||
if (Array.isArray(jsxAttributeAction) && jsxAttributeAction[0] === "rename") {
|
|
||||||
path.node.name.name = jsxAttributeAction[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
type Compile = ReturnType<typeof createPostCSS>;
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export function createPostCSS({
|
|
||||||
tailwindConfig,
|
|
||||||
postCSSPlugins = [],
|
|
||||||
directives,
|
|
||||||
}: Pick<TailwindPluginOptions, "tailwindConfig" | "postCSSPlugins" | "directives">) {
|
|
||||||
const post = postcss([
|
|
||||||
tailwind({
|
|
||||||
...tailwindConfig,
|
|
||||||
content: [{ raw: "<br>", extension: "html" }],
|
|
||||||
}),
|
|
||||||
...postCSSPlugins,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const appliedDirectives = directives === "all" ? tailwindDirectives : directives;
|
|
||||||
const directiveTexts = appliedDirectives?.map(d => `@tailwind ${d};\n`).join("") ?? "";
|
|
||||||
|
|
||||||
return (css: string) => post.process(directiveTexts + css, { from: undefined });
|
|
||||||
}
|
|
||||||
|
|
||||||
const esbuildPlugin = (
|
|
||||||
styleMap: Map<string, string>,
|
|
||||||
compile: Compile
|
|
||||||
): esbuild.Plugin => ({
|
|
||||||
name: PLUGIN_NAME,
|
|
||||||
|
|
||||||
setup(build) {
|
|
||||||
build.onResolve({ filter: /^tailwind:.+\.css$/ }, ({ path, importer }) => {
|
|
||||||
const resolved = join(dirname(importer), path.replace(/^tailwind:/, ""));
|
|
||||||
if (styleMap.has(resolved)) {
|
|
||||||
return {
|
|
||||||
path: resolved,
|
|
||||||
namespace: ESBUILD_NAMESPACE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
build.onResolve({ filter: RegExp(`^${name}/base$`) }, () => ({
|
|
||||||
path: "directive:base",
|
|
||||||
namespace: ESBUILD_NAMESPACE,
|
|
||||||
}));
|
|
||||||
|
|
||||||
build.onLoad({ filter: /.*/, namespace: ESBUILD_NAMESPACE }, async ({ path }) => {
|
|
||||||
if (path === "directive:base") {
|
|
||||||
return {
|
|
||||||
contents: (await compile(`@tailwind base;`)).css,
|
|
||||||
loader: "css",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!styleMap.has(path)) return;
|
|
||||||
const result = await compile(styleMap.get(path)!);
|
|
||||||
|
|
||||||
return {
|
|
||||||
contents: result.css,
|
|
||||||
loader: "css",
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const vitePlugin = (styleMap: Map<string, string>, compile: Compile): vite.Plugin => ({
|
|
||||||
name: "tailwind",
|
|
||||||
resolveId(id, importer) {
|
|
||||||
if (id === `${name}/base`) {
|
|
||||||
return {
|
|
||||||
id: ROLLUP_PREFIX + "directive:base",
|
|
||||||
moduleSideEffects: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id.startsWith("tailwind:")) {
|
|
||||||
const resolved = join(dirname(importer!), id.slice("tailwind:".length));
|
|
||||||
if (styleMap.has(resolved)) {
|
|
||||||
return {
|
|
||||||
id: ROLLUP_PREFIX + resolved,
|
|
||||||
moduleSideEffects: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async load(id: string) {
|
|
||||||
if (id.startsWith(ROLLUP_PREFIX)) {
|
|
||||||
const resolved = id.slice(ROLLUP_PREFIX.length);
|
|
||||||
if (resolved === "directive:base") {
|
|
||||||
return (await compile("@tailwind base;")).css;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styleMap.has(resolved)) {
|
|
||||||
const result = await compile(styleMap.get(resolved)!);
|
|
||||||
return result.css;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry. Returns the plugins and utilities for processing Tailwind
|
* Main entry. Returns the plugins and utilities for processing Tailwind
|
||||||
* classNames in JS.
|
* classNames in JS.
|
||||||
@ -386,7 +89,7 @@ const vitePlugin = (styleMap: Map<string, string>, compile: Compile): vite.Plugi
|
|||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
export function getTailwindPlugins(options: TailwindPluginOptions) {
|
export function getTailwindPlugins(options: TailwindPluginOptions) {
|
||||||
const styleMap = new Map<string, string>();
|
const styleMap: StyleMap = new Map();
|
||||||
const compile = createPostCSS(options);
|
const compile = createPostCSS(options);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
56
src/shared.ts
Normal file
56
src/shared.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import tailwind from "tailwindcss";
|
||||||
|
import postcss from "postcss";
|
||||||
|
import type { TailwindPluginOptions } from "./index";
|
||||||
|
|
||||||
|
export const { name: pkgName } = [require][0](
|
||||||
|
process.env.BABEL_TAILWIND_BUILD ? "./package.json" : "../package.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
interface LineColumn {
|
||||||
|
line: number;
|
||||||
|
column: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SourceLocation {
|
||||||
|
filename: string;
|
||||||
|
start: LineColumn;
|
||||||
|
end: LineColumn;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StyleMapEntry {
|
||||||
|
key: string;
|
||||||
|
className: string;
|
||||||
|
location: SourceLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tailwindDirectives = ["components", "utilities", "variants"] as const;
|
||||||
|
|
||||||
|
export type StyleMap = Map</* filename */ string, StyleMapEntry[]>;
|
||||||
|
|
||||||
|
export function createPostCSS({
|
||||||
|
tailwindConfig,
|
||||||
|
postCSSPlugins = [],
|
||||||
|
directives,
|
||||||
|
}: Pick<TailwindPluginOptions, "tailwindConfig" | "postCSSPlugins" | "directives">) {
|
||||||
|
const post = postcss([
|
||||||
|
tailwind({
|
||||||
|
...tailwindConfig,
|
||||||
|
content: [{ raw: "<br>", extension: "html" }],
|
||||||
|
}),
|
||||||
|
...postCSSPlugins,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const appliedDirectives = directives === "all" ? tailwindDirectives : directives;
|
||||||
|
const directiveTexts = appliedDirectives?.map(d => `@tailwind ${d};\n`).join("") ?? "";
|
||||||
|
|
||||||
|
return (css: string) => post.process(directiveTexts + css, { from: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Compile = ReturnType<typeof createPostCSS>;
|
||||||
|
|
||||||
|
export function toCSSText(tailwindMap: StyleMapEntry[]) {
|
||||||
|
return tailwindMap
|
||||||
|
.map(({ className, key }) => `.${key} {\n @apply ${className}\n}`)
|
||||||
|
.join("\n");
|
||||||
|
}
|
40
src/vite-plugin.ts
Normal file
40
src/vite-plugin.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import type * as vite from "vite";
|
||||||
|
import { type Compile, type StyleMap, pkgName, toCSSText } from "./shared";
|
||||||
|
|
||||||
|
const ROLLUP_PREFIX = "\0tailwind:";
|
||||||
|
|
||||||
|
export const vitePlugin = (styleMap: StyleMap, compile: Compile): vite.Plugin => ({
|
||||||
|
name: "tailwind",
|
||||||
|
resolveId(id, importer) {
|
||||||
|
if (id === `${pkgName}/base`) {
|
||||||
|
return {
|
||||||
|
id: ROLLUP_PREFIX + "directive:base",
|
||||||
|
moduleSideEffects: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.startsWith("tailwind:")) {
|
||||||
|
const resolved = join(dirname(importer!), id.slice("tailwind:".length));
|
||||||
|
if (styleMap.has(resolved)) {
|
||||||
|
return {
|
||||||
|
id: ROLLUP_PREFIX + resolved,
|
||||||
|
moduleSideEffects: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async load(id: string) {
|
||||||
|
if (id.startsWith(ROLLUP_PREFIX)) {
|
||||||
|
const resolved = id.slice(ROLLUP_PREFIX.length);
|
||||||
|
if (resolved === "directive:base") {
|
||||||
|
return (await compile("@tailwind base;")).css;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (styleMap.has(resolved)) {
|
||||||
|
const result = await compile(toCSSText(styleMap.get(resolved)!));
|
||||||
|
return result.css;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user