next-yak 0.0.27 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -503,6 +503,29 @@ const Component2 = styled.div\`
|
|
|
503
503
|
`);
|
|
504
504
|
});
|
|
505
505
|
|
|
506
|
+
it("should inline constants", async () => {
|
|
507
|
+
expect(
|
|
508
|
+
await cssloader.call(
|
|
509
|
+
loaderContext,
|
|
510
|
+
`
|
|
511
|
+
import { styled, css } from "next-yak";
|
|
512
|
+
|
|
513
|
+
const red = "#E50914";
|
|
514
|
+
const zIndex = 14;
|
|
515
|
+
const headline = css\`
|
|
516
|
+
color: \${red};
|
|
517
|
+
z-index: \${zIndex};
|
|
518
|
+
\`
|
|
519
|
+
`
|
|
520
|
+
)
|
|
521
|
+
).toMatchInlineSnapshot(`
|
|
522
|
+
".headline_0 {
|
|
523
|
+
color: #E50914;
|
|
524
|
+
z-index: 14;
|
|
525
|
+
}"
|
|
526
|
+
`);
|
|
527
|
+
});
|
|
528
|
+
|
|
506
529
|
it("should support conditional nested expressions", async () => {
|
|
507
530
|
expect(
|
|
508
531
|
await cssloader.call(
|
|
@@ -510,9 +533,9 @@ const Component2 = styled.div\`
|
|
|
510
533
|
`
|
|
511
534
|
import { styled, keyframes, css } from "next-yak";
|
|
512
535
|
|
|
513
|
-
const color = "green";
|
|
514
|
-
const duration = "1s";
|
|
515
|
-
const easing = "ease-out";
|
|
536
|
+
const color = new Token("green");
|
|
537
|
+
const duration = new Token("1s");
|
|
538
|
+
const easing = new Token("ease-out");
|
|
516
539
|
|
|
517
540
|
const Component = styled.div\`
|
|
518
541
|
background-color: red;
|
|
@@ -461,6 +461,30 @@ const Icon = styled.div\`
|
|
|
461
461
|
`);
|
|
462
462
|
});
|
|
463
463
|
|
|
464
|
+
it("should remove string and number constants from the ts code", async () => {
|
|
465
|
+
expect(
|
|
466
|
+
await tsloader.call(
|
|
467
|
+
loaderContext,
|
|
468
|
+
`
|
|
469
|
+
import { styled, css } from "next-yak";
|
|
470
|
+
|
|
471
|
+
const red = "#E50914";
|
|
472
|
+
const zIndex = 14;
|
|
473
|
+
const headline = css\`
|
|
474
|
+
color: \${red};
|
|
475
|
+
z-index: \${zIndex};
|
|
476
|
+
\`
|
|
477
|
+
`
|
|
478
|
+
)
|
|
479
|
+
).toMatchInlineSnapshot(`
|
|
480
|
+
"import { styled, css } from \\"next-yak\\";
|
|
481
|
+
import __styleYak from \\"./page.yak.module.css!=!./page?./page.yak.module.css\\";
|
|
482
|
+
const red = \\"#E50914\\";
|
|
483
|
+
const zIndex = 14;
|
|
484
|
+
const headline = css(__styleYak.headline_0);"
|
|
485
|
+
`)
|
|
486
|
+
});
|
|
487
|
+
|
|
464
488
|
it("should show error when using a runtime value from top level", async () => {
|
|
465
489
|
await expect(() =>
|
|
466
490
|
tsloader.call(
|
|
@@ -491,7 +515,7 @@ const headline = css\`
|
|
|
491
515
|
`
|
|
492
516
|
import { styled, css } from "next-yak";
|
|
493
517
|
|
|
494
|
-
const $red = "#E50914";
|
|
518
|
+
const $red = new Token("#E50914");
|
|
495
519
|
const Button = styled.button\`
|
|
496
520
|
\${({ $primary, $digits }) => {
|
|
497
521
|
return $primary && css\`
|
|
@@ -6,6 +6,10 @@ const { relative, resolve, basename } = require("path");
|
|
|
6
6
|
const localIdent = require("./lib/localIdent.cjs");
|
|
7
7
|
const getStyledComponentName = require("./lib/getStyledComponentName.cjs");
|
|
8
8
|
const getCssName = require("./lib/getCssName.cjs");
|
|
9
|
+
const {
|
|
10
|
+
getConstantName,
|
|
11
|
+
getConstantValues,
|
|
12
|
+
} = require("./lib/getConstantValues.cjs");
|
|
9
13
|
|
|
10
14
|
/** @typedef {{replaces: Record<string, unknown>, rootContext?: string}} YakBabelPluginOptions */
|
|
11
15
|
/** @typedef {{ css: string | undefined, styled: string | undefined, keyframes: string | undefined }} YakLocalIdentifierNames */
|
|
@@ -21,7 +25,7 @@ const getCssName = require("./lib/getCssName.cjs");
|
|
|
21
25
|
* localVarNames: YakLocalIdentifierNames,
|
|
22
26
|
* isImportedInCurrentFile: boolean,
|
|
23
27
|
* classNameCount: number,
|
|
24
|
-
* topLevelConstBindings:
|
|
28
|
+
* topLevelConstBindings: Map<string, number | string | null>,
|
|
25
29
|
* varIndex: number,
|
|
26
30
|
* variableNameToStyledCall: Map<string, {
|
|
27
31
|
* wasAdded: boolean,
|
|
@@ -123,32 +127,11 @@ module.exports = function (babel, options) {
|
|
|
123
127
|
this.classNameCount = 0;
|
|
124
128
|
this.varIndex = 0;
|
|
125
129
|
this.variableNameToStyledCall = new Map();
|
|
126
|
-
this.topLevelConstBindings = new
|
|
130
|
+
this.topLevelConstBindings = new Map();
|
|
127
131
|
},
|
|
128
132
|
visitor: {
|
|
129
|
-
Program(path
|
|
130
|
-
|
|
131
|
-
const constBindings = bindings.filter(([_name, binding]) => {
|
|
132
|
-
if (binding.kind === "module") {
|
|
133
|
-
// it is unclear if this is a const binding
|
|
134
|
-
// however to be safe, all module bindings are considered consts
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
return (
|
|
138
|
-
// let and var might change but this would be very strange react code
|
|
139
|
-
// as states must use useState or useExternalStore
|
|
140
|
-
(binding.kind === "let" ||
|
|
141
|
-
binding.kind === "var" ||
|
|
142
|
-
binding.kind === "const") &&
|
|
143
|
-
// don't consider functions or arrow functions as constants
|
|
144
|
-
(!("init" in binding.path.node) ||
|
|
145
|
-
(!t.isFunctionDeclaration(binding.path.node.init) &&
|
|
146
|
-
!t.isArrowFunctionExpression(binding.path.node.init)))
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
constBindings.forEach(([name]) => {
|
|
150
|
-
this.topLevelConstBindings.add(name);
|
|
151
|
-
});
|
|
133
|
+
Program(path) {
|
|
134
|
+
this.topLevelConstBindings = getConstantValues(path, t);
|
|
152
135
|
},
|
|
153
136
|
/**
|
|
154
137
|
* Store the name of the imported 'css' and 'styled' variables e.g.:
|
|
@@ -354,26 +337,14 @@ module.exports = function (babel, options) {
|
|
|
354
337
|
wasInsideCssValue = true;
|
|
355
338
|
// to prevent overuse of css variables, we only allow expressions
|
|
356
339
|
// for css variables for arrow function expressions
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
// e.g. styled.div`color: ${x()};`
|
|
366
|
-
(t.isCallExpression(expression) &&
|
|
367
|
-
// x()
|
|
368
|
-
((t.isIdentifier(expression.callee) &&
|
|
369
|
-
this.topLevelConstBindings.has(expression.callee.name)) ||
|
|
370
|
-
// x.y()
|
|
371
|
-
(t.isMemberExpression(expression.callee) &&
|
|
372
|
-
t.isIdentifier(expression.callee.object) &&
|
|
373
|
-
this.topLevelConstBindings.has(
|
|
374
|
-
expression.callee.object.name
|
|
375
|
-
))))
|
|
376
|
-
) {
|
|
340
|
+
const variableName = getConstantName(expression, t);
|
|
341
|
+
if (variableName && this.topLevelConstBindings.has(variableName)) {
|
|
342
|
+
// Ignore constants that have a static string or number value
|
|
343
|
+
const value = this.topLevelConstBindings.get(variableName);
|
|
344
|
+
if (value !== null) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
377
348
|
throw new InvalidPositionError(
|
|
378
349
|
"Possible constant used as runtime value for a css variable\n" +
|
|
379
350
|
"Please move the constant to a .yak import or use an arrow function\n" +
|
package/loaders/cssloader.cjs
CHANGED
|
@@ -8,6 +8,10 @@ const getStyledComponentName = require("./lib/getStyledComponentName.cjs");
|
|
|
8
8
|
const getCssName = require("./lib/getCssName.cjs");
|
|
9
9
|
const murmurhash2_32_gc = require("./lib/hash.cjs");
|
|
10
10
|
const { relative } = require("path");
|
|
11
|
+
const {
|
|
12
|
+
getConstantName,
|
|
13
|
+
getConstantValues,
|
|
14
|
+
} = require("./lib/getConstantValues.cjs");
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* @typedef {{ selector: string, hasParent: boolean, quasiCode: Array<{ insideCssValue: boolean, code: string }>, cssPartExpressions: { [key: number]: CssPartExpression[]} }} CssPartExpression
|
|
@@ -86,7 +90,14 @@ module.exports = async function cssLoader(source) {
|
|
|
86
90
|
/** @type {Map<string, string>} */
|
|
87
91
|
const variableNameToStyledClassName = new Map();
|
|
88
92
|
|
|
93
|
+
/** @type {Map<string, number | string | null>} */
|
|
94
|
+
let topLevelConstBindings = new Map();
|
|
95
|
+
|
|
96
|
+
|
|
89
97
|
babel.traverse(ast, {
|
|
98
|
+
Program(path) {
|
|
99
|
+
topLevelConstBindings = getConstantValues(path, t);
|
|
100
|
+
},
|
|
90
101
|
/**
|
|
91
102
|
* @param {import("@babel/core").NodePath<import("@babel/types").ImportDeclaration>} path
|
|
92
103
|
*/
|
|
@@ -240,13 +251,22 @@ module.exports = async function cssLoader(source) {
|
|
|
240
251
|
const relativePath = relative(rootContext, resourcePath);
|
|
241
252
|
hashedFile = murmurhash2_32_gc(relativePath);
|
|
242
253
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
254
|
+
const variableName = t.isExpression(expression) && getConstantName(expression, t);
|
|
255
|
+
const variableConstValue = variableName && topLevelConstBindings.get(variableName);
|
|
256
|
+
if (variableConstValue === null || variableConstValue === undefined) {
|
|
257
|
+
currentCssParts.quasiCode.push({
|
|
258
|
+
insideCssValue: true,
|
|
259
|
+
code:
|
|
260
|
+
unEscapeCssCode(quasi.value.raw) +
|
|
261
|
+
// replace the expression with a css variable
|
|
262
|
+
`var(--🦬${hashedFile}${varIndex++})`,
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
currentCssParts.quasiCode.push({
|
|
266
|
+
insideCssValue: true,
|
|
267
|
+
code: unEscapeCssCode(quasi.value.raw) + String(variableConstValue),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
250
270
|
} else {
|
|
251
271
|
wasInsideCssValue = false;
|
|
252
272
|
// code is added
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** @typedef {import("@babel/types")} babel */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the name of the expression
|
|
7
|
+
* @param {babel.types.Expression} expression
|
|
8
|
+
* @param {import("@babel/types")} t
|
|
9
|
+
*/
|
|
10
|
+
const getConstantName = (expression, t) => {
|
|
11
|
+
// e.g. styled.div`color: ${x};`
|
|
12
|
+
if (t.isIdentifier(expression)) {
|
|
13
|
+
// e.g. x
|
|
14
|
+
return expression.name;
|
|
15
|
+
} else if (
|
|
16
|
+
t.isMemberExpression(expression) &&
|
|
17
|
+
t.isIdentifier(expression.object)
|
|
18
|
+
) {
|
|
19
|
+
// e.g. x for x.y
|
|
20
|
+
return expression.object.name;
|
|
21
|
+
} else if (
|
|
22
|
+
t.isCallExpression(expression) &&
|
|
23
|
+
t.isIdentifier(expression.callee)
|
|
24
|
+
) {
|
|
25
|
+
// e.g. x for x()
|
|
26
|
+
return expression.callee.name;
|
|
27
|
+
} else if (
|
|
28
|
+
t.isCallExpression(expression) &&
|
|
29
|
+
t.isMemberExpression(expression.callee) &&
|
|
30
|
+
t.isIdentifier(expression.callee.object)
|
|
31
|
+
) {
|
|
32
|
+
// e.g. x for x.y()
|
|
33
|
+
return expression.callee.object.name;
|
|
34
|
+
} else {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extracts all top level constant values from a program path
|
|
41
|
+
*
|
|
42
|
+
* @param {babel.NodePath<babel.types.Program>} path
|
|
43
|
+
* @param {import("@babel/types")} t
|
|
44
|
+
*/
|
|
45
|
+
function getConstantValues(path, t) {
|
|
46
|
+
/** @type {Map<string, string | number | null>} */
|
|
47
|
+
const topLevelConstBindings = new Map();
|
|
48
|
+
const bindings = Object.entries(path.scope.bindings);
|
|
49
|
+
for (const [name, binding] of bindings) {
|
|
50
|
+
if (binding.kind === "module") {
|
|
51
|
+
topLevelConstBindings.set(name, null);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
binding.kind === "let" ||
|
|
56
|
+
binding.kind === "var" ||
|
|
57
|
+
binding.kind === "const"
|
|
58
|
+
) {
|
|
59
|
+
// don't consider function declarations or arrow functions as constants
|
|
60
|
+
if (
|
|
61
|
+
!("init" in binding.path.node) ||
|
|
62
|
+
t.isFunctionDeclaration(binding.path.node.init) ||
|
|
63
|
+
t.isArrowFunctionExpression(binding.path.node.init)
|
|
64
|
+
) {
|
|
65
|
+
topLevelConstBindings.set(name, null);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const value = binding.path.node.init;
|
|
69
|
+
topLevelConstBindings.set(
|
|
70
|
+
name,
|
|
71
|
+
t.isStringLiteral(value) || t.isNumericLiteral(value)
|
|
72
|
+
? value.value
|
|
73
|
+
: null
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return topLevelConstBindings;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
getConstantValues,
|
|
82
|
+
getConstantName,
|
|
83
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-yak",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/",
|
|
6
6
|
"exports": {
|
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"prepublishOnly": "npm run build && npm run test && npm run test:types:code && npm run test:types:test",
|
|
35
|
+
"build": "pnpm run --filter . \"/^build:/\"",
|
|
36
|
+
"build:runtime": "tsup runtime/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
37
|
+
"build:runtime:types": "tsup runtime/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
38
|
+
"build:static": "tsup static/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
39
|
+
"build:static:types": "tsup static/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
40
|
+
"build:baseContext": "tsup runtime/context/baseContext.tsx --target es2022 --external react --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
41
|
+
"build:context:client": "tsup runtime/context/index.tsx --target es2022 --external react --external next-yak/context/baseContext --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
42
|
+
"build:context:server": "tsup runtime/context/index.server.tsx --target es2022 --external react --external next-yak/context/baseContext --external ./index.js --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
43
|
+
"build:withYak": "tsup withYak/index.ts --target es2022 --dts --format cjs --sourcemap --out-dir withYak --external ../loaders/tsloader.cjs --external ../loaders/cssloader.cjs",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:types:code": "tsc -p tsconfig.json",
|
|
46
|
+
"test:types:test": "tsc -p ./runtime/__tests__/tsconfig.json",
|
|
47
|
+
"test:watch": "vitest --watch"
|
|
48
|
+
},
|
|
33
49
|
"dependencies": {
|
|
34
50
|
"@babel/core": "7.23.2",
|
|
35
51
|
"@babel/plugin-syntax-typescript": "7.22.5",
|
|
@@ -58,20 +74,5 @@
|
|
|
58
74
|
"loaders",
|
|
59
75
|
"runtime",
|
|
60
76
|
"withYak"
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
"build": "pnpm run --filter . \"/^build:/\"",
|
|
64
|
-
"build:runtime": "tsup runtime/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
65
|
-
"build:runtime:types": "tsup runtime/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist",
|
|
66
|
-
"build:static": "tsup static/index.ts --target es2022 --clean --external react --external next-yak/context --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
67
|
-
"build:static:types": "tsup static/index.ts --target es2022 --clean --external react --dts-only --format cjs,esm --minify --sourcemap --out-dir dist/static",
|
|
68
|
-
"build:baseContext": "tsup runtime/context/baseContext.tsx --target es2022 --external react --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
69
|
-
"build:context:client": "tsup runtime/context/index.tsx --target es2022 --external react --external next-yak/context/baseContext --dts --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
70
|
-
"build:context:server": "tsup runtime/context/index.server.tsx --target es2022 --external react --external next-yak/context/baseContext --external ./index.js --format cjs,esm --sourcemap --out-dir dist/context/",
|
|
71
|
-
"build:withYak": "tsup withYak/index.ts --target es2022 --dts --format cjs --sourcemap --out-dir withYak --external ../loaders/tsloader.cjs --external ../loaders/cssloader.cjs",
|
|
72
|
-
"test": "vitest run",
|
|
73
|
-
"test:types:code": "tsc -p tsconfig.json",
|
|
74
|
-
"test:types:test": "tsc -p ./runtime/__tests__/tsconfig.json",
|
|
75
|
-
"test:watch": "vitest --watch"
|
|
76
|
-
}
|
|
77
|
-
}
|
|
77
|
+
]
|
|
78
|
+
}
|