Skip to content

Commit 075f145

Browse files
committed
feat: support tailwindcss postcss7-compat
1 parent 37159d1 commit 075f145

File tree

13 files changed

+905
-170
lines changed

13 files changed

+905
-170
lines changed

.vscode/launch.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7+
{
8+
"command": "pnpm start",
9+
"name": "[scripts] postcss7-compat run",
10+
"request": "launch",
11+
"type": "node-terminal",
12+
"cwd": "${workspaceFolder}/scripts/postcss7-compat"
13+
},
714
{
815
"command": "yarn dev",
916
"name": "[vite-vue] dev",

eslint.config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { icebreaker } from '@icebreakers/eslint-config'
22

3-
export default icebreaker({}, {
3+
export default icebreaker({
4+
tailwindcss: false,
5+
}, {
46
ignores: ['**/fixtures/**'],
57
})

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"eslint-plugin-prettier": "^5.1.3",
4141
"lodash-es": "^4.17.21",
4242
"only-allow": "^1.2.1",
43+
"pathe": "^1.1.2",
4344
"prettier": "^3.3.0",
4445
"rollup": "^4.18.0",
4546
"tailwindcss-patch": "workspace:*",

packages/tailwindcss-patch/src/core/inspector-postcss7-compat.ts

Lines changed: 61 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { generate, parse, traverse } from '@/babel'
33
// crash code
44

55
export function inspectProcessTailwindFeaturesReturnContext(content: string) {
6-
const ast = parse(content)
6+
const ast = parse(content, {
7+
sourceType: 'unambiguous',
8+
})
79
let hasPatched = false
810
traverse(ast, {
911
FunctionDeclaration(p) {
@@ -39,20 +41,17 @@ export function inspectPostcssPlugin(content: string) {
3941
traverse(ast, {
4042
Program(p) {
4143
const n = p.node
42-
// find module.exports = function tailwindcss(configOrPath)
44+
// find function _default(configOrPath = {}) {
4345
const idx = n.body.findIndex((x) => {
4446
return (
45-
t.isExpressionStatement(x)
46-
&& t.isAssignmentExpression(x.expression)
47-
&& t.isMemberExpression(x.expression.left)
48-
&& t.isFunctionExpression(x.expression.right)
49-
&& x.expression.right.id?.name === 'tailwindcss'
47+
t.isFunctionDeclaration(x)
48+
&& x.id?.name === '_default'
5049
)
5150
})
5251

5352
if (idx > -1) {
5453
const prevStatement = n.body[idx - 1]
55-
const lastStatement = n.body[n.body.length - 1]
54+
const lastStatement = n.body[idx - 2]
5655
const hasPatchedCondition0
5756
= prevStatement
5857
&& t.isVariableDeclaration(prevStatement)
@@ -70,108 +69,87 @@ export function inspectPostcssPlugin(content: string) {
7069
// const contextRef = {
7170
// value: []
7271
// };
73-
const statement = t.variableDeclaration('const', [
72+
const statement = t.variableDeclaration('var', [
7473
t.variableDeclarator(t.identifier(variableName), t.objectExpression([t.objectProperty(t.identifier(valueKey), t.arrayExpression())])),
7574
])
76-
n.body.splice(idx, 0, statement)
77-
// module.exports.contextRef = contextRef;
78-
n.body.push(
75+
n.body.splice(idx, 0, statement,
76+
// exports.contextRef = contextRef;
7977
t.expressionStatement(
8078
t.assignmentExpression(
8179
'=',
82-
t.memberExpression(t.memberExpression(t.identifier('module'), t.identifier('exports')), t.identifier(exportKey)),
80+
t.memberExpression(t.identifier('exports'), t.identifier(exportKey)),
8381
t.identifier(variableName),
8482
),
85-
),
86-
)
83+
))
8784
}
8885
}
8986
},
90-
FunctionExpression(p) {
87+
FunctionDeclaration(p) {
9188
if (hasPatched) {
9289
return
9390
}
9491
const n = p.node
95-
if (n.id?.name === 'tailwindcss' && n.body.body.length === 1 && t.isReturnStatement(n.body.body[0])) {
92+
if (n.id?.name === '_default' && n.body.body.length === 1 && t.isReturnStatement(n.body.body[0])) {
9693
const returnStatement = n.body.body[0]
97-
if (t.isObjectExpression(returnStatement.argument) && returnStatement.argument.properties.length === 2) {
98-
const properties = returnStatement.argument.properties
99-
if (t.isObjectProperty(properties[0]) && t.isObjectProperty(properties[1])) {
100-
const keyMatched = t.isIdentifier(properties[0].key) && properties[0].key.name === 'postcssPlugin'
101-
const pluginsMatched = t.isIdentifier(properties[1].key) && properties[1].key.name === 'plugins'
102-
if (
103-
pluginsMatched
104-
&& keyMatched
105-
&& t.isCallExpression(properties[1].value)
106-
&& t.isMemberExpression(properties[1].value.callee)
107-
&& t.isArrayExpression(properties[1].value.callee.object)
108-
) {
109-
const pluginsCode = properties[1].value.callee.object.elements
110-
if (pluginsCode[1] && t.isFunctionExpression(pluginsCode[1])) {
111-
const targetBlockStatement = pluginsCode[1].body
94+
if (t.isCallExpression(returnStatement.argument) && t.isMemberExpression(returnStatement.argument.callee) && t.isArrayExpression(returnStatement.argument.callee.object)) {
95+
const targetFn = returnStatement.argument.callee.object.elements[1]
96+
if (t.isFunctionExpression(targetFn)) {
97+
// 函数体
98+
const targetBlockStatement = targetFn.body
11299

113-
const lastStatement = targetBlockStatement.body[targetBlockStatement.body.length - 1]
114-
if (t.isExpressionStatement(lastStatement)) {
115-
// contextRef.value.push((0, _processTailwindFeatures.default)(context)(root, result));
116-
const newExpressionStatement = t.expressionStatement(
117-
t.callExpression(
118-
t.memberExpression(
119-
t.memberExpression(t.identifier(variableName), t.identifier('value')),
100+
const lastStatement = targetBlockStatement.body[targetBlockStatement.body.length - 1]
101+
if (t.isExpressionStatement(lastStatement)) {
102+
// contextRef.value.push((0, _processTailwindFeatures.default)(context)(root, result));
103+
const newExpressionStatement = t.expressionStatement(
104+
t.callExpression(
105+
t.memberExpression(
106+
t.memberExpression(t.identifier(variableName), t.identifier('value')),
120107

121-
t.identifier('push'),
122-
),
108+
t.identifier('push'),
109+
),
123110

124-
[lastStatement.expression],
125-
),
126-
)
127-
targetBlockStatement.body[targetBlockStatement.body.length - 1] = newExpressionStatement
128-
}
111+
[lastStatement.expression],
112+
),
113+
)
114+
targetBlockStatement.body[targetBlockStatement.body.length - 1] = newExpressionStatement
115+
}
129116

130-
const ifIdx = targetBlockStatement.body.findIndex(x => t.isIfStatement(x))
131-
if (ifIdx > -1) {
132-
const ifRoot = <t.IfStatement>targetBlockStatement.body[ifIdx]
133-
if (t.isBlockStatement(ifRoot.consequent) && ifRoot.consequent.body[1] && t.isForOfStatement(ifRoot.consequent.body[1])) {
134-
const forOf: t.ForOfStatement = ifRoot.consequent.body[1]
135-
if (t.isBlockStatement(forOf.body) && forOf.body.body.length === 1 && t.isIfStatement(forOf.body.body[0])) {
136-
const if2: t.IfStatement = forOf.body.body[0]
137-
if (t.isBlockStatement(if2.consequent) && if2.consequent.body.length === 1 && t.isExpressionStatement(if2.consequent.body[0])) {
138-
const target = if2.consequent.body[0]
139-
// contextRef.value.push((0, _processTailwindFeatures.default)(context)(root1, result));
140-
const newExpressionStatement = t.expressionStatement(
141-
t.callExpression(t.memberExpression(t.memberExpression(t.identifier(variableName), t.identifier('value')), t.identifier('push')), [target.expression]),
142-
)
143-
if2.consequent.body[0] = newExpressionStatement
144-
}
145-
}
117+
const ifIdx = targetBlockStatement.body.findIndex(x => t.isIfStatement(x))
118+
if (ifIdx > -1) {
119+
const ifRoot = <t.IfStatement>targetBlockStatement.body[ifIdx]
120+
if (t.isBlockStatement(ifRoot.consequent) && ifRoot.consequent.body[1] && t.isForOfStatement(ifRoot.consequent.body[1])) {
121+
const forOf: t.ForOfStatement = ifRoot.consequent.body[1]
122+
if (t.isBlockStatement(forOf.body) && forOf.body.body.length === 1 && t.isIfStatement(forOf.body.body[0])) {
123+
const if2: t.IfStatement = forOf.body.body[0]
124+
if (t.isBlockStatement(if2.consequent) && if2.consequent.body.length === 1 && t.isExpressionStatement(if2.consequent.body[0])) {
125+
const target = if2.consequent.body[0]
126+
// contextRef.value.push((0, _processTailwindFeatures.default)(context)(root1, result));
127+
const newExpressionStatement = t.expressionStatement(
128+
t.callExpression(t.memberExpression(t.memberExpression(t.identifier(variableName), t.identifier('value')), t.identifier('push')), [target.expression]),
129+
)
130+
if2.consequent.body[0] = newExpressionStatement
146131
}
147132
}
148-
// clear contextRef.value
149-
targetBlockStatement.body.unshift(
150-
// contentRef.value = []
151-
// t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(variableName), t.identifier(valueKey)), t.arrayExpression()))
152-
153-
// contentRef.value.length = 0
154-
t.expressionStatement(
155-
t.assignmentExpression(
156-
'=',
157-
t.memberExpression(t.memberExpression(t.identifier(variableName), t.identifier(valueKey)), t.identifier('length')),
158-
t.numericLiteral(0),
159-
),
160-
),
161-
)
162133
}
163134
}
135+
// clear contextRef.value
136+
targetBlockStatement.body.unshift(
137+
// contentRef.value = []
138+
// t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(variableName), t.identifier(valueKey)), t.arrayExpression()))
139+
140+
// contentRef.value.length = 0
141+
t.expressionStatement(
142+
t.assignmentExpression(
143+
'=',
144+
t.memberExpression(t.memberExpression(t.identifier(variableName), t.identifier(valueKey)), t.identifier('length')),
145+
t.numericLiteral(0),
146+
),
147+
),
148+
)
164149
}
165150
}
166151
}
167-
// start = true
168152
},
169-
// BlockStatement(p) {
170-
// const n = p.node
171-
// if (start && p.parent.type === 'FunctionExpression' && !p.parent.id) {
172-
// n.body.unshift(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(variableName), t.identifier(valueKey)), t.arrayExpression())))
173-
// }
174-
// }
175153
})
176154
return {
177155
code: hasPatched ? content : generate(ast).code,

packages/tailwindcss-patch/src/core/runtime-patcher.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,39 @@ export function monkeyPatchForExposingContextV3(twDir: string, opt: InternalPatc
8383
return result
8484
}
8585

86+
export function monkeyPatchForExposingContextV2(twDir: string, opt: InternalPatchOptions) {
87+
const processTailwindFeaturesFilePath = path.resolve(twDir, 'lib/jit/processTailwindFeatures.js')
88+
89+
const processTailwindFeaturesContent = ensureFileContent(processTailwindFeaturesFilePath)
90+
const result: { processTailwindFeatures?: string, plugin?: string } & Record<string, any> = {}
91+
if (processTailwindFeaturesContent) {
92+
const { code, hasPatched } = inspectProcessTailwindFeaturesReturnContext(processTailwindFeaturesContent)
93+
if (!hasPatched && opt.overwrite) {
94+
fs.writeFileSync(processTailwindFeaturesFilePath, code, {
95+
encoding: 'utf8',
96+
})
97+
console.log('patch tailwindcss processTailwindFeatures for return content successfully!')
98+
}
99+
result.processTailwindFeatures = code
100+
}
101+
102+
const indexFilePath = path.resolve(twDir, 'lib/jit/index.js')
103+
const pluginContent = ensureFileContent([indexFilePath])
104+
if (pluginContent) {
105+
const { code, hasPatched } = inspectPostcssPlugin(pluginContent)
106+
if (!hasPatched && opt.overwrite) {
107+
fs.writeFileSync(indexFilePath, code, {
108+
encoding: 'utf8',
109+
})
110+
console.log('patch tailwindcss for expose runtime content successfully!')
111+
}
112+
result.plugin = code
113+
}
114+
115+
opt.custom && typeof opt.custom === 'function' && opt.custom(twDir, result)
116+
return result
117+
}
118+
86119
export function internalPatch(pkgJsonPath: string | undefined, options: InternalPatchOptions): any | undefined {
87120
if (pkgJsonPath) {
88121
// eslint-disable-next-line ts/no-var-requires, ts/no-require-imports
@@ -95,8 +128,8 @@ export function internalPatch(pkgJsonPath: string | undefined, options: Internal
95128
}
96129
else if (gte(pkgJson.version!, '2.0.0')) {
97130
options.version = pkgJson.version
98-
// const result = monkeyPatchForExposingContext(twDir, options)
99-
// return result
131+
const result = monkeyPatchForExposingContextV2(twDir, options)
132+
return result
100133
}
101134
// no sth
102135
}
Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,94 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`postcss7-compat > common 1`] = `""`;
3+
exports[`postcss7-compat > jit plugins patch 1`] = `
4+
""use strict";
5+
6+
Object.defineProperty(exports, "__esModule", {
7+
value: true
8+
});
9+
exports.default = _default;
10+
var _setupTrackingContext = _interopRequireDefault(require("./lib/setupTrackingContext"));
11+
var _setupWatchingContext = _interopRequireDefault(require("./lib/setupWatchingContext"));
12+
var _sharedState = require("./lib/sharedState");
13+
var _processTailwindFeatures = _interopRequireDefault(require("./processTailwindFeatures"));
14+
function _interopRequireDefault(obj) {
15+
return obj && obj.__esModule ? obj : {
16+
default: obj
17+
};
18+
}
19+
var contextRef = {
20+
value: []
21+
};
22+
exports.contextRef = contextRef;
23+
function _default(configOrPath = {}) {
24+
return [_sharedState.env.DEBUG && function (root) {
25+
console.log('\\n');
26+
console.time('JIT TOTAL');
27+
return root;
28+
}, function (root, result) {
29+
contextRef.value.length = 0;
30+
let setupContext = _sharedState.env.TAILWIND_MODE === 'watch' ? (0, _setupWatchingContext.default)(configOrPath) : (0, _setupTrackingContext.default)(configOrPath);
31+
contextRef.value.push((0, _processTailwindFeatures.default)(setupContext)(root, result));
32+
}, _sharedState.env.DEBUG && function (root) {
33+
console.timeEnd('JIT TOTAL');
34+
console.log('\\n');
35+
return root;
36+
}].filter(Boolean);
37+
}"
38+
`;
39+
40+
exports[`postcss7-compat > processTailwindFeatures patch 1`] = `
41+
""use strict";
42+
43+
Object.defineProperty(exports, "__esModule", {
44+
value: true
45+
});
46+
exports.default = processTailwindFeatures;
47+
var _normalizeTailwindDirectives = _interopRequireDefault(require("./lib/normalizeTailwindDirectives"));
48+
var _expandTailwindAtRules = _interopRequireDefault(require("./lib/expandTailwindAtRules"));
49+
var _expandApplyAtRules = _interopRequireDefault(require("./lib/expandApplyAtRules"));
50+
var _evaluateTailwindFunctions = _interopRequireDefault(require("../lib/evaluateTailwindFunctions"));
51+
var _substituteScreenAtRules = _interopRequireDefault(require("../lib/substituteScreenAtRules"));
52+
var _resolveDefaultsAtRules = _interopRequireDefault(require("./lib/resolveDefaultsAtRules"));
53+
var _collapseAdjacentRules = _interopRequireDefault(require("./lib/collapseAdjacentRules"));
54+
var _setupContextUtils = require("./lib/setupContextUtils");
55+
var _log = _interopRequireDefault(require("../util/log"));
56+
function _interopRequireDefault(obj) {
57+
return obj && obj.__esModule ? obj : {
58+
default: obj
59+
};
60+
}
61+
let warned = false;
62+
function processTailwindFeatures(setupContext) {
63+
return function (root, result) {
64+
if (!warned) {
65+
_log.default.warn([\`You have enabled the JIT engine which is currently in preview.\`, 'Preview features are not covered by semver, may introduce breaking changes, and can change at any time.']);
66+
warned = true;
67+
}
68+
let tailwindDirectives = (0, _normalizeTailwindDirectives.default)(root);
69+
let context = setupContext({
70+
tailwindDirectives,
71+
registerDependency(dependency) {
72+
result.messages.push({
73+
plugin: 'tailwindcss',
74+
parent: result.opts.from,
75+
...dependency
76+
});
77+
},
78+
createContext(tailwindConfig, changedContent) {
79+
return (0, _setupContextUtils.createContext)(tailwindConfig, changedContent, tailwindDirectives, root);
80+
}
81+
})(root, result);
82+
if (context.tailwindConfig.separator === '-') {
83+
throw new Error("The '-' character cannot be used as a custom separator in JIT mode due to parsing ambiguity. Please use another character like '_' instead.");
84+
}
85+
(0, _expandTailwindAtRules.default)(context)(root, result);
86+
(0, _expandApplyAtRules.default)(context)(root, result);
87+
(0, _evaluateTailwindFunctions.default)(context)(root, result);
88+
(0, _substituteScreenAtRules.default)(context)(root, result);
89+
(0, _resolveDefaultsAtRules.default)(context)(root, result);
90+
(0, _collapseAdjacentRules.default)(context)(root, result);
91+
return context;
92+
};
93+
}"
94+
`;

packages/tailwindcss-patch/test/exposeContext.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import path from 'node:path'
1+
import path from 'pathe'
22
import { getTailwindcssEntry } from '@/core/exposeContext'
33

44
describe('exposeContext', () => {

packages/tailwindcss-patch/test/fixtures/postcss7-compat/lib/jit/processTailwindFeatures.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ function processTailwindFeatures(setupContext) {
3636
}
3737

3838
let tailwindDirectives = (0, _normalizeTailwindDirectives.default)(root);
39-
let
40-
= setupContext({
39+
let context = setupContext({
4140
tailwindDirectives,
4241

4342
registerDependency(dependency) {

0 commit comments

Comments
 (0)