@pre.style/core 3.0.0-alpha.0 → 3.1.0-alpha.1
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.
- package/README.md +332 -76
- package/dist/compiler/block.d.ts +30 -0
- package/dist/compiler/block.js +86 -26
- package/dist/compiler/compiler.d.ts +16 -0
- package/dist/compiler/compiler.js +294 -25
- package/dist/compiler/config.d.ts +16 -0
- package/dist/compiler/config.js +61 -0
- package/dist/compiler/index.d.ts +6 -1
- package/dist/compiler/index.js +3 -0
- package/dist/compiler/keyframes.d.ts +6 -0
- package/dist/compiler/keyframes.js +120 -0
- package/dist/compiler/libraries.d.ts +16 -0
- package/dist/compiler/libraries.js +76 -0
- package/dist/compiler/mapper.d.ts +1 -0
- package/dist/compiler/mapper.js +165 -0
- package/dist/compiler/stylesheet.d.ts +15 -0
- package/dist/compiler/stylesheet.js +244 -0
- package/package.json +15 -3
- package/package.schema.json +117 -0
package/dist/compiler/block.d.ts
CHANGED
|
@@ -31,9 +31,22 @@ export interface GlobalRule {
|
|
|
31
31
|
overrideKey: string | null;
|
|
32
32
|
position: Position | null;
|
|
33
33
|
}
|
|
34
|
+
export interface Compose {
|
|
35
|
+
names: string[];
|
|
36
|
+
from: {
|
|
37
|
+
type: 'local';
|
|
38
|
+
} | {
|
|
39
|
+
type: 'global';
|
|
40
|
+
} | {
|
|
41
|
+
type: 'file';
|
|
42
|
+
path: string;
|
|
43
|
+
};
|
|
44
|
+
position: Position | null;
|
|
45
|
+
}
|
|
34
46
|
export interface ParsedBlock {
|
|
35
47
|
atoms: Atom[];
|
|
36
48
|
globals: GlobalRule[];
|
|
49
|
+
composes: Compose[];
|
|
37
50
|
}
|
|
38
51
|
export declare class PreStyleError extends Error {
|
|
39
52
|
readonly position: Position | null;
|
|
@@ -41,12 +54,14 @@ export declare class PreStyleError extends Error {
|
|
|
41
54
|
}
|
|
42
55
|
export interface SassCompiler {
|
|
43
56
|
compileString(source: string, options?: {
|
|
57
|
+
url?: URL;
|
|
44
58
|
logger?: {
|
|
45
59
|
warn?(message: string): void;
|
|
46
60
|
debug?(message: string): void;
|
|
47
61
|
};
|
|
48
62
|
}): {
|
|
49
63
|
css: string;
|
|
64
|
+
loadedUrls?: URL[];
|
|
50
65
|
};
|
|
51
66
|
}
|
|
52
67
|
export interface ParseOptions {
|
|
@@ -54,9 +69,24 @@ export interface ParseOptions {
|
|
|
54
69
|
prepend: Record<Lang, string>;
|
|
55
70
|
sass: SassCompiler | null;
|
|
56
71
|
warn: (message: string) => void;
|
|
72
|
+
keyframeLocalization: boolean;
|
|
57
73
|
}
|
|
74
|
+
export declare const key: (value: unknown) => string;
|
|
58
75
|
export declare function parseBlock(code: string, lang: Lang, options: ParseOptions): ParsedBlock;
|
|
76
|
+
export declare function parseRules(css: string, lightningcss: LightningOptions, position: ToPosition, collect: (rules: Rule[]) => void): void;
|
|
77
|
+
export declare function dropOverridden(atoms: Atom[]): Atom[];
|
|
78
|
+
export type ToPosition = (loc: {
|
|
79
|
+
line: number;
|
|
80
|
+
column: number;
|
|
81
|
+
} | undefined) => Position | null;
|
|
59
82
|
export declare const valueOf: (rule: Rule) => Record<string, any>;
|
|
83
|
+
export declare function walk(rules: Rule[], atRules: AtRuleSegment[], variant: SelectorList | null, parsed: ParsedBlock, position: ToPosition): void;
|
|
84
|
+
export declare function addDeclarations(block: {
|
|
85
|
+
declarations?: Declaration[];
|
|
86
|
+
importantDeclarations?: Declaration[];
|
|
87
|
+
} | undefined, atRules: AtRuleSegment[], selectors: SelectorList, atoms: Atom[], composes: Compose[] | null, position: Position | null): void;
|
|
88
|
+
export declare function addGlobal(rule: Rule, atRules: AtRuleSegment[], globals: GlobalRule[], position: ToPosition): void;
|
|
89
|
+
export declare function overrideKey(rule: Rule): string;
|
|
60
90
|
export declare function propertyOf(declaration: Declaration): {
|
|
61
91
|
property: string;
|
|
62
92
|
shorthand: boolean;
|
package/dist/compiler/block.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { composeVisitors, transform } from 'lightningcss';
|
|
2
2
|
import { atRuleInfo } from './at-rules.js';
|
|
3
3
|
import { expand } from './expand.js';
|
|
4
|
+
import { localizeKeyframes } from './keyframes.js';
|
|
4
5
|
import { compose, fromPlaceholder, isElement, PLACEHOLDER, repair } from './selectors.js';
|
|
5
6
|
import { splitBlock } from './split.js';
|
|
6
7
|
import { SHORTHANDS } from '../shorthands.js';
|
|
@@ -14,7 +15,7 @@ export class PreStyleError extends Error {
|
|
|
14
15
|
}
|
|
15
16
|
// JSON keys used to deduplicate. Locations are left out, so identical rules
|
|
16
17
|
// from different places share a key.
|
|
17
|
-
const key = (value) => JSON.stringify(value, (k, v) => (k === 'loc' ? undefined : v));
|
|
18
|
+
export const key = (value) => JSON.stringify(value, (k, v) => (k === 'loc' ? undefined : v));
|
|
18
19
|
const HOIST_ORDER = { charset: 0, layer: 1, import: 2, namespace: 3 };
|
|
19
20
|
const hoistOrder = (rule) => HOIST_ORDER[/^@([\w-]+)/.exec(rule)[1].toLowerCase()] ?? Infinity;
|
|
20
21
|
// Joins rules onto one line.
|
|
@@ -78,38 +79,46 @@ export function parseBlock(code, lang, options) {
|
|
|
78
79
|
});
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
const parsed = { atoms: [], globals: [] };
|
|
82
|
+
const parsed = { atoms: [], globals: [], composes: [] };
|
|
82
83
|
// Sass output positions don't match the block, so scss atoms have none.
|
|
83
84
|
const position = (loc) => lang === 'css' && loc ? toBlock(loc.line, loc.column) : null;
|
|
84
|
-
|
|
85
|
-
const
|
|
85
|
+
parseRules(css, options.lightningcss, position, (rules) => walk(rules, [], null, parsed, position));
|
|
86
|
+
const atoms = dropOverridden(parsed.atoms);
|
|
87
|
+
if (!options.keyframeLocalization)
|
|
88
|
+
return { atoms, globals: parsed.globals, composes: parsed.composes };
|
|
89
|
+
const local = localizeKeyframes(parsed.globals);
|
|
90
|
+
return { atoms: local.atoms(atoms), globals: local.globals, composes: parsed.composes };
|
|
91
|
+
}
|
|
92
|
+
// Parses CSS with Lightning CSS, and passes its rules to `collect` once the
|
|
93
|
+
// user's visitor has changed them. `position` converts error locations.
|
|
94
|
+
export function parseRules(css, lightningcss, position, collect) {
|
|
95
|
+
const exit = {
|
|
86
96
|
StyleSheetExit(sheet) {
|
|
87
|
-
|
|
97
|
+
collect(sheet.rules);
|
|
88
98
|
},
|
|
89
99
|
};
|
|
90
|
-
const { visitor, ...rest } =
|
|
100
|
+
const { visitor, ...rest } = lightningcss;
|
|
91
101
|
try {
|
|
92
102
|
transform({
|
|
93
103
|
...rest,
|
|
94
104
|
filename: 'block.css',
|
|
95
105
|
code: Buffer.from(css),
|
|
96
|
-
visitor: visitor ? composeVisitors([visitor,
|
|
106
|
+
visitor: visitor ? composeVisitors([visitor, exit]) : exit,
|
|
97
107
|
});
|
|
98
108
|
}
|
|
99
109
|
catch (error) {
|
|
110
|
+
if (error instanceof PreStyleError)
|
|
111
|
+
throw error;
|
|
100
112
|
const loc = error.loc;
|
|
101
113
|
// Lightning CSS reports error lines from 1.
|
|
102
|
-
throw new PreStyleError(`Invalid CSS: ${error.message}`,
|
|
103
|
-
cause: error,
|
|
104
|
-
});
|
|
114
|
+
throw new PreStyleError(`Invalid CSS: ${error.message}`, loc ? position({ line: loc.line - 1, column: loc.column }) : null, { cause: error });
|
|
105
115
|
}
|
|
106
|
-
return { atoms: dropOverridden(parsed.atoms), globals: parsed.globals };
|
|
107
116
|
}
|
|
108
117
|
// Within a block, a later declaration overrides an earlier one that sets the
|
|
109
118
|
// same property under the same at-rules and selector variant, as in CSS.
|
|
110
119
|
// `!important` declarations are collected after normal ones in each rule,
|
|
111
120
|
// so they win. Identical atoms are kept once.
|
|
112
|
-
function dropOverridden(atoms) {
|
|
121
|
+
export function dropOverridden(atoms) {
|
|
113
122
|
const seen = new Set();
|
|
114
123
|
const kept = [];
|
|
115
124
|
for (let i = atoms.length - 1; i >= 0; i--) {
|
|
@@ -129,14 +138,15 @@ function dropOverridden(atoms) {
|
|
|
129
138
|
export const valueOf = (rule) => 'value' in rule && rule.value !== null ? rule.value : {};
|
|
130
139
|
const ruleLoc = (rule) => valueOf(rule).loc;
|
|
131
140
|
// Walks the rules. `variant` is the element's selector variant when inside a
|
|
132
|
-
// rule that targets the element, and null outside one.
|
|
133
|
-
|
|
141
|
+
// rule that targets the element, and null outside one. Without the
|
|
142
|
+
// placeholder, every rule is global.
|
|
143
|
+
export function walk(rules, atRules, variant, parsed, position) {
|
|
134
144
|
for (const rule of rules) {
|
|
135
145
|
if (rule.type === 'style') {
|
|
136
146
|
const { selectors, declarations, rules: children = [], loc } = rule.value;
|
|
137
147
|
if (variant !== null) {
|
|
138
148
|
const composed = compose(variant, selectors);
|
|
139
|
-
addDeclarations(declarations, atRules, composed, parsed, position(loc));
|
|
149
|
+
addDeclarations(declarations, atRules, composed, parsed.atoms, null, position(loc));
|
|
140
150
|
walk(children, atRules, composed, parsed, position);
|
|
141
151
|
continue;
|
|
142
152
|
}
|
|
@@ -151,15 +161,17 @@ function walk(rules, atRules, variant, parsed, position) {
|
|
|
151
161
|
other.push(selector);
|
|
152
162
|
}
|
|
153
163
|
if (other.length > 0)
|
|
154
|
-
addGlobal({ ...rule, value: { ...rule.value, selectors: other } }, atRules, parsed, position);
|
|
164
|
+
addGlobal({ ...rule, value: { ...rule.value, selectors: other } }, atRules, parsed.globals, position);
|
|
155
165
|
if (element.length > 0) {
|
|
156
|
-
|
|
166
|
+
// `composes` only works directly in the block, outside at-rules.
|
|
167
|
+
const composes = atRules.length === 0 && isElement(element) ? parsed.composes : null;
|
|
168
|
+
addDeclarations(declarations, atRules, element, parsed.atoms, composes, position(loc));
|
|
157
169
|
walk(children, atRules, element, parsed, position);
|
|
158
170
|
}
|
|
159
171
|
continue;
|
|
160
172
|
}
|
|
161
173
|
if (rule.type === 'nested-declarations' && variant !== null) {
|
|
162
|
-
addDeclarations(rule.value.declarations, atRules, variant, parsed, position(rule.value.loc));
|
|
174
|
+
addDeclarations(rule.value.declarations, atRules, variant, parsed.atoms, null, position(rule.value.loc));
|
|
163
175
|
continue;
|
|
164
176
|
}
|
|
165
177
|
if (atRuleInfo(rule.type).atomic) {
|
|
@@ -171,20 +183,40 @@ function walk(rules, atRules, variant, parsed, position) {
|
|
|
171
183
|
walk(children, [...atRules, segment], variant, parsed, position);
|
|
172
184
|
continue;
|
|
173
185
|
}
|
|
174
|
-
addGlobal(rule, atRules, parsed, position);
|
|
186
|
+
addGlobal(rule, atRules, parsed.globals, position);
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
|
-
|
|
189
|
+
const COMPOSES_PLACEMENT = "`composes` must come before the rule's other declarations, and can't be used inside nested selectors or at-rules.";
|
|
190
|
+
const isComposes = (declaration) => declaration.property === 'unparsed' && declaration.value.propertyId.property === 'composes';
|
|
191
|
+
// Adds a rule's declarations as atoms. `composes` declarations are added to
|
|
192
|
+
// `composes` when it's given, and are an error where it isn't (inside
|
|
193
|
+
// nested selectors and at-rules).
|
|
194
|
+
export function addDeclarations(block, atRules, selectors, atoms, composes, position) {
|
|
178
195
|
let variant = null;
|
|
179
196
|
if (!isElement(selectors)) {
|
|
180
197
|
const repaired = repair(selectors);
|
|
181
198
|
variant = { key: key(repaired), selectors: repaired };
|
|
182
199
|
}
|
|
200
|
+
if (block?.importantDeclarations?.some(isComposes))
|
|
201
|
+
throw new PreStyleError("`composes` can't be `!important`.", position);
|
|
202
|
+
let normal = block?.declarations ?? [];
|
|
203
|
+
// As in CSS Modules, `composes` comes before the rule's other declarations.
|
|
204
|
+
const leading = normal.findIndex((d) => !isComposes(d));
|
|
205
|
+
const own = leading === -1 ? normal.length : leading;
|
|
206
|
+
if (normal.slice(own).some(isComposes))
|
|
207
|
+
throw new PreStyleError(COMPOSES_PLACEMENT, position);
|
|
208
|
+
if (own > 0) {
|
|
209
|
+
if (composes === null)
|
|
210
|
+
throw new PreStyleError(COMPOSES_PLACEMENT, position);
|
|
211
|
+
for (const d of normal.slice(0, own))
|
|
212
|
+
composes.push(parseComposes(d, position));
|
|
213
|
+
normal = normal.slice(own);
|
|
214
|
+
}
|
|
183
215
|
const add = (declarations = [], important) => {
|
|
184
216
|
for (const declaration of declarations) {
|
|
185
217
|
for (const value of expand(declaration)) {
|
|
186
218
|
const { property, shorthand } = propertyOf(value);
|
|
187
|
-
|
|
219
|
+
atoms.push({
|
|
188
220
|
atRules,
|
|
189
221
|
variant,
|
|
190
222
|
declaration: { key: key(value) + (important ? '!' : ''), value, important },
|
|
@@ -195,11 +227,38 @@ function addDeclarations(block, atRules, selectors, parsed, position) {
|
|
|
195
227
|
}
|
|
196
228
|
}
|
|
197
229
|
};
|
|
198
|
-
add(
|
|
230
|
+
add(normal, false);
|
|
199
231
|
add(block?.importantDeclarations, true);
|
|
200
232
|
}
|
|
233
|
+
const ident = (t) => t?.type === 'token' && t.value.type === 'ident' ? t.value.value : null;
|
|
234
|
+
// Reads `composes: a b`, `composes: a from './file.css'`, or
|
|
235
|
+
// `composes: a from global`.
|
|
236
|
+
function parseComposes(declaration, position) {
|
|
237
|
+
const tokens = declaration.value.value.filter((t) => !(t.type === 'token' && t.value.type === 'white-space'));
|
|
238
|
+
const invalid = () => new PreStyleError('Invalid `composes`. Use `composes: a b;`, `composes: a from "./file.css";`, or `composes: a from global;`.', position);
|
|
239
|
+
const names = [];
|
|
240
|
+
let i = 0;
|
|
241
|
+
for (; i < tokens.length; i++) {
|
|
242
|
+
const name = ident(tokens[i]);
|
|
243
|
+
if (name === null || name === 'from')
|
|
244
|
+
break;
|
|
245
|
+
names.push(name);
|
|
246
|
+
}
|
|
247
|
+
if (names.length === 0)
|
|
248
|
+
throw invalid();
|
|
249
|
+
if (i === tokens.length)
|
|
250
|
+
return { names, from: { type: 'local' }, position };
|
|
251
|
+
if (ident(tokens[i]) !== 'from' || tokens.length !== i + 2)
|
|
252
|
+
throw invalid();
|
|
253
|
+
const source = tokens[i + 1];
|
|
254
|
+
if (ident(source) === 'global')
|
|
255
|
+
return { names, from: { type: 'global' }, position };
|
|
256
|
+
if (source.type === 'token' && source.value.type === 'string')
|
|
257
|
+
return { names, from: { type: 'file', path: source.value.value }, position };
|
|
258
|
+
throw invalid();
|
|
259
|
+
}
|
|
201
260
|
// Global rules inside at-rules keep their at-rules around them.
|
|
202
|
-
function addGlobal(rule, atRules,
|
|
261
|
+
export function addGlobal(rule, atRules, globals, position) {
|
|
203
262
|
let wrapped = rule;
|
|
204
263
|
for (const segment of atRules.toReversed()) {
|
|
205
264
|
wrapped = {
|
|
@@ -209,7 +268,7 @@ function addGlobal(rule, atRules, parsed, position) {
|
|
|
209
268
|
}
|
|
210
269
|
const info = atRules.length === 0 ? atRuleInfo(rule.type) : atRuleInfo('');
|
|
211
270
|
wrapped = repair(wrapped);
|
|
212
|
-
|
|
271
|
+
globals.push({
|
|
213
272
|
key: key(wrapped),
|
|
214
273
|
rule: wrapped,
|
|
215
274
|
hoist: info.hoist,
|
|
@@ -218,11 +277,12 @@ function addGlobal(rule, atRules, parsed, position) {
|
|
|
218
277
|
});
|
|
219
278
|
}
|
|
220
279
|
// The identity of an overridable at-rule: its type and name.
|
|
221
|
-
function overrideKey(rule) {
|
|
280
|
+
export function overrideKey(rule) {
|
|
222
281
|
const value = valueOf(rule);
|
|
223
282
|
switch (rule.type) {
|
|
224
283
|
case 'keyframes':
|
|
225
|
-
|
|
284
|
+
// `@-webkit-keyframes x` doesn't replace `@keyframes x`.
|
|
285
|
+
return `keyframes ${key(value.vendorPrefix ?? [])} ${key(value.name)}`;
|
|
226
286
|
case 'font-face': {
|
|
227
287
|
// A font face is identified by its family, style, weight, and stretch.
|
|
228
288
|
const descriptors = Object.fromEntries(value.properties
|
|
@@ -8,6 +8,7 @@ export type PrependFiles = string[] | {
|
|
|
8
8
|
export interface Library {
|
|
9
9
|
dir: string;
|
|
10
10
|
prependFiles?: PrependFiles;
|
|
11
|
+
keyframeLocalization?: boolean;
|
|
11
12
|
}
|
|
12
13
|
export interface CompilerOptions {
|
|
13
14
|
prependFiles?: PrependFiles;
|
|
@@ -17,6 +18,8 @@ export interface CompilerOptions {
|
|
|
17
18
|
root?: string;
|
|
18
19
|
warn?: (message: string) => void;
|
|
19
20
|
quirksMode?: boolean;
|
|
21
|
+
keyframeLocalization?: boolean;
|
|
22
|
+
layer?: false | string;
|
|
20
23
|
}
|
|
21
24
|
export interface Block {
|
|
22
25
|
code: string;
|
|
@@ -25,14 +28,27 @@ export interface Block {
|
|
|
25
28
|
line?: number;
|
|
26
29
|
column?: number;
|
|
27
30
|
}
|
|
31
|
+
export interface Stylesheet {
|
|
32
|
+
code: string;
|
|
33
|
+
lang: Lang;
|
|
34
|
+
file: string;
|
|
35
|
+
}
|
|
36
|
+
export interface CompiledStylesheet {
|
|
37
|
+
exports: [name: string, classString: string][];
|
|
38
|
+
dependencies: string[];
|
|
39
|
+
}
|
|
28
40
|
export declare function createCompiler(options?: CompilerOptions): Promise<Compiler>;
|
|
29
41
|
export declare class Compiler {
|
|
30
42
|
#private;
|
|
31
43
|
constructor(options: CompilerOptions, sass: SassCompiler | null);
|
|
32
44
|
get revision(): number;
|
|
33
45
|
scan(block: Block): void;
|
|
46
|
+
scanStylesheet(sheet: Stylesheet): void;
|
|
34
47
|
assignNames(): void;
|
|
35
48
|
classString(block: Block): string;
|
|
49
|
+
dependencies(block: Block): string[];
|
|
50
|
+
classNames(sheet: Stylesheet): string[];
|
|
51
|
+
stylesheet(sheet: Stylesheet): CompiledStylesheet;
|
|
36
52
|
css(): Printed;
|
|
37
53
|
cnTable(): CnTable;
|
|
38
54
|
}
|