ripple 0.1.1 → 0.2.0
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/LICENSE +21 -0
- package/package.json +56 -24
- package/src/ai.js +292 -0
- package/src/compiler/errors.js +26 -0
- package/src/compiler/index.js +26 -0
- package/src/compiler/phases/1-parse/index.js +543 -0
- package/src/compiler/phases/1-parse/style.js +566 -0
- package/src/compiler/phases/2-analyze/index.js +509 -0
- package/src/compiler/phases/2-analyze/prune.js +572 -0
- package/src/compiler/phases/3-transform/index.js +1572 -0
- package/src/compiler/phases/3-transform/segments.js +91 -0
- package/src/compiler/phases/3-transform/stylesheet.js +372 -0
- package/src/compiler/scope.js +421 -0
- package/src/compiler/utils.js +552 -0
- package/src/constants.js +4 -0
- package/src/jsx-runtime.d.ts +94 -0
- package/src/jsx-runtime.js +46 -0
- package/src/runtime/array.js +215 -0
- package/src/runtime/index.js +39 -0
- package/src/runtime/internal/client/blocks.js +247 -0
- package/src/runtime/internal/client/constants.js +23 -0
- package/src/runtime/internal/client/events.js +223 -0
- package/src/runtime/internal/client/for.js +388 -0
- package/src/runtime/internal/client/if.js +35 -0
- package/src/runtime/internal/client/index.js +53 -0
- package/src/runtime/internal/client/operations.js +72 -0
- package/src/runtime/internal/client/portal.js +33 -0
- package/src/runtime/internal/client/render.js +156 -0
- package/src/runtime/internal/client/runtime.js +909 -0
- package/src/runtime/internal/client/template.js +51 -0
- package/src/runtime/internal/client/try.js +139 -0
- package/src/runtime/internal/client/utils.js +16 -0
- package/src/utils/ast.js +214 -0
- package/src/utils/builders.js +733 -0
- package/src/utils/patterns.js +23 -0
- package/src/utils/sanitize_template_string.js +7 -0
- package/test-mappings.js +0 -0
- package/types/index.d.ts +2 -0
- package/.npmignore +0 -2
- package/History.md +0 -3
- package/Readme.md +0 -151
- package/lib/exec/index.js +0 -60
- package/ripple.js +0 -645
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { decode } from '@jridgewell/sourcemap-codec';
|
|
2
|
+
|
|
3
|
+
export const defaultMappingData = {
|
|
4
|
+
verification: true,
|
|
5
|
+
completion: true,
|
|
6
|
+
semantic: true,
|
|
7
|
+
navigation: true
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Convert esrap SourceMap to Volar mappings using Svelte's approach
|
|
12
|
+
* Based on: https://github.com/volarjs/svelte-language-tools/blob/master/packages/language-server/src/language.ts#L45-L88
|
|
13
|
+
* @param {object} source_map - esrap SourceMap object
|
|
14
|
+
* @param {string} source - Original Ripple source
|
|
15
|
+
* @param {string} generated_code - Generated TypeScript code
|
|
16
|
+
* @returns {object} Object with code and mappings for Volar
|
|
17
|
+
*/
|
|
18
|
+
export function convert_source_map_to_mappings(source_map, source, generated_code) {
|
|
19
|
+
const mappings = [];
|
|
20
|
+
|
|
21
|
+
// Decode the VLQ mappings from esrap
|
|
22
|
+
const decoded_mappings = decode(source_map.mappings);
|
|
23
|
+
|
|
24
|
+
let generated_offset = 0;
|
|
25
|
+
const generated_lines = generated_code.split('\n');
|
|
26
|
+
|
|
27
|
+
// Process each line of generated code
|
|
28
|
+
for (let generated_line = 0; generated_line < generated_lines.length; generated_line++) {
|
|
29
|
+
const line = generated_lines[generated_line];
|
|
30
|
+
const line_mappings = decoded_mappings[generated_line] || [];
|
|
31
|
+
|
|
32
|
+
// Process mappings for this line
|
|
33
|
+
for (const mapping of line_mappings) {
|
|
34
|
+
const [generated_column, source_file_index, source_line, source_column] = mapping;
|
|
35
|
+
|
|
36
|
+
// Skip mappings without source information
|
|
37
|
+
if (source_file_index == null || source_line == null || source_column == null) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Calculate source offset
|
|
42
|
+
const source_lines = source.split('\n');
|
|
43
|
+
let source_offset = 0;
|
|
44
|
+
for (let i = 0; i < Math.min(source_line, source_lines.length - 1); i++) {
|
|
45
|
+
source_offset += source_lines[i].length + 1; // +1 for newline
|
|
46
|
+
}
|
|
47
|
+
source_offset += source_column;
|
|
48
|
+
|
|
49
|
+
// Calculate generated offset
|
|
50
|
+
const current_generated_offset = generated_offset + generated_column;
|
|
51
|
+
|
|
52
|
+
// Determine segment length (look ahead to next mapping or end of line)
|
|
53
|
+
const next_mapping = line_mappings[line_mappings.indexOf(mapping) + 1];
|
|
54
|
+
let segment_length = next_mapping ? next_mapping[0] - generated_column : Math.max(1, line.length - generated_column);
|
|
55
|
+
|
|
56
|
+
// Determine the actual segment content
|
|
57
|
+
const generated_content = generated_code.substring(current_generated_offset, current_generated_offset + segment_length);
|
|
58
|
+
const source_content = source.substring(source_offset, source_offset + segment_length);
|
|
59
|
+
|
|
60
|
+
// Fix for $children mapping: when generated content is "$children",
|
|
61
|
+
// it should only map to the component name in the source, not include attributes
|
|
62
|
+
if (generated_content === '$children') {
|
|
63
|
+
// Look for the component name in the source content
|
|
64
|
+
const component_name_match = source_content.match(/^(\w+)/);
|
|
65
|
+
if (component_name_match) {
|
|
66
|
+
const component_name = component_name_match[1];
|
|
67
|
+
segment_length = component_name.length;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create Volar mapping with default mapping data
|
|
72
|
+
mappings.push({
|
|
73
|
+
sourceOffsets: [source_offset],
|
|
74
|
+
generatedOffsets: [current_generated_offset],
|
|
75
|
+
lengths: [segment_length],
|
|
76
|
+
data: defaultMappingData
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Add line length + 1 for newline (except for last line)
|
|
81
|
+
generated_offset += line.length;
|
|
82
|
+
if (generated_line < generated_lines.length - 1) {
|
|
83
|
+
generated_offset += 1; // newline character
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
code: generated_code,
|
|
89
|
+
mappings
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import { walk } from 'zimmerframe';
|
|
3
|
+
|
|
4
|
+
const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/;
|
|
5
|
+
|
|
6
|
+
const is_keyframes_node = (node) => remove_css_prefix(node.name) === 'keyframes';
|
|
7
|
+
|
|
8
|
+
function remove_css_prefix(name) {
|
|
9
|
+
return name.replace(regex_css_browser_prefix, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function remove_preceding_whitespace(end, state) {
|
|
13
|
+
let start = end;
|
|
14
|
+
while (/\s/.test(state.code.original[start - 1])) start--;
|
|
15
|
+
if (start < end) state.code.remove(start, end);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function is_used(rule) {
|
|
19
|
+
return rule.prelude.children.some((selector) => selector.metadata.used);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function is_in_global_block(path) {
|
|
23
|
+
return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function remove_global_pseudo_class(selector, combinator, state) {
|
|
27
|
+
if (selector.args === null) {
|
|
28
|
+
let start = selector.start;
|
|
29
|
+
if (combinator?.name === ' ') {
|
|
30
|
+
// div :global.x becomes div.x
|
|
31
|
+
while (/\s/.test(state.code.original[start - 1])) start--;
|
|
32
|
+
}
|
|
33
|
+
state.code.remove(start, selector.start + ':global'.length);
|
|
34
|
+
} else {
|
|
35
|
+
state.code
|
|
36
|
+
.remove(selector.start, selector.start + ':global('.length)
|
|
37
|
+
.remove(selector.end - 1, selector.end);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function escape_comment_close(node, code) {
|
|
42
|
+
let escaped = false;
|
|
43
|
+
let in_comment = false;
|
|
44
|
+
|
|
45
|
+
for (let i = node.start; i < node.end; i++) {
|
|
46
|
+
if (escaped) {
|
|
47
|
+
escaped = false;
|
|
48
|
+
} else {
|
|
49
|
+
const char = code.original[i];
|
|
50
|
+
if (in_comment) {
|
|
51
|
+
if (char === '*' && code.original[i + 1] === '/') {
|
|
52
|
+
code.prependRight(++i, '\\');
|
|
53
|
+
in_comment = false;
|
|
54
|
+
}
|
|
55
|
+
} else if (char === '\\') {
|
|
56
|
+
escaped = true;
|
|
57
|
+
} else if (char === '/' && code.original[++i] === '*') {
|
|
58
|
+
in_comment = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {AST.CSS.Rule} rule
|
|
66
|
+
* @param {boolean} is_in_global_block
|
|
67
|
+
*/
|
|
68
|
+
function is_empty(rule, is_in_global_block) {
|
|
69
|
+
if (rule.metadata.is_global_block) {
|
|
70
|
+
return rule.block.children.length === 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const child of rule.block.children) {
|
|
74
|
+
if (child.type === 'Declaration') {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (child.type === 'Rule') {
|
|
79
|
+
if ((is_used(child) || is_in_global_block) && !is_empty(child, is_in_global_block)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (child.type === 'Atrule') {
|
|
85
|
+
if (child.block === null || child.block.children.length > 0) return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const visitors = {
|
|
93
|
+
_: (node, context) => {
|
|
94
|
+
context.state.code.addSourcemapLocation(node.start);
|
|
95
|
+
context.state.code.addSourcemapLocation(node.end);
|
|
96
|
+
context.next();
|
|
97
|
+
},
|
|
98
|
+
Atrule(node, { state, next, path }) {
|
|
99
|
+
if (is_keyframes_node(node)) {
|
|
100
|
+
let start = node.start + node.name.length + 1;
|
|
101
|
+
while (state.code.original[start] === ' ') start += 1;
|
|
102
|
+
let end = start;
|
|
103
|
+
while (state.code.original[end] !== '{' && state.code.original[end] !== ' ') end += 1;
|
|
104
|
+
|
|
105
|
+
if (node.prelude.startsWith('-global-')) {
|
|
106
|
+
state.code.remove(start, start + 8);
|
|
107
|
+
} else if (!is_in_global_block(path)) {
|
|
108
|
+
state.code.prependRight(start, `${state.hash}-`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return; // don't transform anything within
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
next();
|
|
115
|
+
},
|
|
116
|
+
Declaration(node, { state }) {
|
|
117
|
+
const property = node.property && remove_css_prefix(node.property.toLowerCase());
|
|
118
|
+
if (property === 'animation' || property === 'animation-name') {
|
|
119
|
+
let index = node.start + node.property.length + 1;
|
|
120
|
+
let name = '';
|
|
121
|
+
|
|
122
|
+
while (index < state.code.original.length) {
|
|
123
|
+
const character = state.code.original[index];
|
|
124
|
+
|
|
125
|
+
if (regex_css_name_boundary.test(character)) {
|
|
126
|
+
if (state.keyframes.includes(name)) {
|
|
127
|
+
state.code.prependRight(index - name.length, `${state.hash}-`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (character === ';' || character === '}') {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
name = '';
|
|
135
|
+
} else {
|
|
136
|
+
name += character;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
index++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
Rule(node, { state, next, visit, path }) {
|
|
144
|
+
if (is_empty(node, is_in_global_block(path))) {
|
|
145
|
+
state.code.prependRight(node.start, '/* (empty) ');
|
|
146
|
+
state.code.appendLeft(node.end, '*/');
|
|
147
|
+
escape_comment_close(node, state.code);
|
|
148
|
+
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!is_used(node) && !is_in_global_block(path)) {
|
|
153
|
+
state.code.prependRight(node.start, '/* (unused) ');
|
|
154
|
+
state.code.appendLeft(node.end, '*/');
|
|
155
|
+
escape_comment_close(node, state.code);
|
|
156
|
+
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (node.metadata.is_global_block) {
|
|
161
|
+
const selector = node.prelude.children[0];
|
|
162
|
+
|
|
163
|
+
if (selector.children.length === 1 && selector.children[0].selectors.length === 1) {
|
|
164
|
+
// `:global {...}`
|
|
165
|
+
if (state.minify) {
|
|
166
|
+
state.code.remove(node.start, node.block.start + 1);
|
|
167
|
+
state.code.remove(node.block.end - 1, node.end);
|
|
168
|
+
} else {
|
|
169
|
+
state.code.prependRight(node.start, '/* ');
|
|
170
|
+
state.code.appendLeft(node.block.start + 1, '*/');
|
|
171
|
+
|
|
172
|
+
state.code.prependRight(node.block.end - 1, '/*');
|
|
173
|
+
state.code.appendLeft(node.block.end, '*/');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// don't recurse into selectors but visit the body
|
|
177
|
+
visit(node.block);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
next();
|
|
183
|
+
},
|
|
184
|
+
SelectorList(node, { state, next, path }) {
|
|
185
|
+
// Only add comments if we're not inside a complex selector that itself is unused or a global block
|
|
186
|
+
if (
|
|
187
|
+
!is_in_global_block(path) &&
|
|
188
|
+
!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)
|
|
189
|
+
) {
|
|
190
|
+
const children = node.children;
|
|
191
|
+
let pruning = false;
|
|
192
|
+
let prune_start = children[0].start;
|
|
193
|
+
let last = prune_start;
|
|
194
|
+
let has_previous_used = false;
|
|
195
|
+
|
|
196
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
197
|
+
const selector = children[i];
|
|
198
|
+
|
|
199
|
+
if (selector.metadata.used === pruning) {
|
|
200
|
+
if (pruning) {
|
|
201
|
+
let i = selector.start;
|
|
202
|
+
while (state.code.original[i] !== ',') i--;
|
|
203
|
+
|
|
204
|
+
if (state.minify) {
|
|
205
|
+
state.code.remove(prune_start, has_previous_used ? i : i + 1);
|
|
206
|
+
} else {
|
|
207
|
+
state.code.appendRight(has_previous_used ? i : i + 1, '*/');
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
if (i === 0) {
|
|
211
|
+
if (state.minify) {
|
|
212
|
+
prune_start = selector.start;
|
|
213
|
+
} else {
|
|
214
|
+
state.code.prependRight(selector.start, '/* (unused) ');
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
if (state.minify) {
|
|
218
|
+
prune_start = last;
|
|
219
|
+
} else {
|
|
220
|
+
state.code.overwrite(last, selector.start, ` /* (unused) `);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
pruning = !pruning;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!pruning && selector.metadata.used) {
|
|
229
|
+
has_previous_used = true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
last = selector.end;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (pruning) {
|
|
236
|
+
if (state.minify) {
|
|
237
|
+
state.code.remove(prune_start, last);
|
|
238
|
+
} else {
|
|
239
|
+
state.code.appendLeft(last, '*/');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// if we're in a `:is(...)` or whatever, keep existing specificity bump state
|
|
245
|
+
let specificity = state.specificity;
|
|
246
|
+
|
|
247
|
+
// if this selector list belongs to a rule, require a specificity bump for the
|
|
248
|
+
// first scoped selector but only if we're at the top level
|
|
249
|
+
let parent = path.at(-1);
|
|
250
|
+
if (parent?.type === 'Rule') {
|
|
251
|
+
specificity = { bumped: false };
|
|
252
|
+
|
|
253
|
+
/** @type {AST.CSS.Rule | null} */
|
|
254
|
+
let rule = parent.metadata.parent_rule;
|
|
255
|
+
|
|
256
|
+
while (rule) {
|
|
257
|
+
if (rule.metadata.has_local_selectors) {
|
|
258
|
+
specificity = { bumped: true };
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
rule = rule.metadata.parent_rule;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
next({ ...state, specificity });
|
|
266
|
+
},
|
|
267
|
+
ComplexSelector(node, context) {
|
|
268
|
+
const before_bumped = context.state.specificity.bumped;
|
|
269
|
+
|
|
270
|
+
for (const relative_selector of node.children) {
|
|
271
|
+
if (relative_selector.metadata.is_global) {
|
|
272
|
+
const global = /** @type {AST.CSS.PseudoClassSelector} */ (relative_selector.selectors[0]);
|
|
273
|
+
remove_global_pseudo_class(global, relative_selector.combinator, context.state);
|
|
274
|
+
|
|
275
|
+
if (
|
|
276
|
+
node.metadata.rule?.metadata.parent_rule &&
|
|
277
|
+
global.args === null &&
|
|
278
|
+
relative_selector.combinator === null
|
|
279
|
+
) {
|
|
280
|
+
// div { :global.x { ... } } becomes div { &.x { ... } }
|
|
281
|
+
context.state.code.prependRight(global.start, '&');
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
} else {
|
|
285
|
+
// for any :global() or :global at the middle of compound selector
|
|
286
|
+
for (const selector of relative_selector.selectors) {
|
|
287
|
+
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
|
|
288
|
+
remove_global_pseudo_class(selector, null, context.state);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (relative_selector.metadata.scoped) {
|
|
294
|
+
if (relative_selector.selectors.length === 1) {
|
|
295
|
+
// skip standalone :is/:where/& selectors
|
|
296
|
+
const selector = relative_selector.selectors[0];
|
|
297
|
+
if (
|
|
298
|
+
selector.type === 'PseudoClassSelector' &&
|
|
299
|
+
(selector.name === 'is' || selector.name === 'where')
|
|
300
|
+
) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (relative_selector.selectors.some((s) => s.type === 'NestingSelector')) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// for the first occurrence, we use a classname selector, so that every
|
|
310
|
+
// encapsulated selector gets a +0-1-0 specificity bump. thereafter,
|
|
311
|
+
// we use a `:where` selector, which does not affect specificity
|
|
312
|
+
let modifier = context.state.selector;
|
|
313
|
+
if (context.state.specificity.bumped) modifier = `:where(${modifier})`;
|
|
314
|
+
|
|
315
|
+
context.state.specificity.bumped = true;
|
|
316
|
+
|
|
317
|
+
let i = relative_selector.selectors.length;
|
|
318
|
+
while (i--) {
|
|
319
|
+
const selector = relative_selector.selectors[i];
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
selector.type === 'PseudoElementSelector' ||
|
|
323
|
+
selector.type === 'PseudoClassSelector'
|
|
324
|
+
) {
|
|
325
|
+
if (selector.name !== 'root' && selector.name !== 'host') {
|
|
326
|
+
if (i === 0) context.state.code.prependRight(selector.start, modifier);
|
|
327
|
+
}
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (selector.type === 'TypeSelector' && selector.name === '*') {
|
|
332
|
+
context.state.code.update(selector.start, selector.end, modifier);
|
|
333
|
+
} else {
|
|
334
|
+
context.state.code.appendLeft(selector.end, modifier);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
context.next();
|
|
343
|
+
|
|
344
|
+
context.state.specificity.bumped = before_bumped;
|
|
345
|
+
},
|
|
346
|
+
PseudoClassSelector(node, context) {
|
|
347
|
+
if (node.name === 'is' || node.name === 'where' || node.name === 'has' || node.name === 'not') {
|
|
348
|
+
context.next();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export function render_stylesheets(stylesheets) {
|
|
354
|
+
let css = '';
|
|
355
|
+
|
|
356
|
+
for (const stylesheet of stylesheets) {
|
|
357
|
+
const code = new MagicString(stylesheet.source);
|
|
358
|
+
const state = {
|
|
359
|
+
code,
|
|
360
|
+
hash: stylesheet.hash,
|
|
361
|
+
selector: `.${stylesheet.hash}`,
|
|
362
|
+
specificity: {
|
|
363
|
+
bumped: false
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
walk(stylesheet, state, visitors);
|
|
368
|
+
css += code.toString();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return css;
|
|
372
|
+
}
|