@animus-ui/layergate 0.0.0-stage → 0.1.36
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 +239 -2
- package/dist/compile-C4GyCiuY.cjs +323 -0
- package/dist/compile-Cu_GFS3l.mjs +295 -0
- package/dist/compile-DZwVBjTM.d.cts +126 -0
- package/dist/compile-DZwVBjTM.d.cts.map +1 -0
- package/dist/compile-DZwVBjTM.d.mts +126 -0
- package/dist/compile-DZwVBjTM.d.mts.map +1 -0
- package/dist/host-BGcb1rMJ.mjs +169 -0
- package/dist/host-C4ZaMdkx.cjs +210 -0
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/plan-Bsn_yxS9.mjs +29 -0
- package/dist/plan-hvk04qzh.cjs +40 -0
- package/dist/plan-w-y0limy.d.cts +35 -0
- package/dist/plan-w-y0limy.d.cts.map +1 -0
- package/dist/plan-w-y0limy.d.mts +35 -0
- package/dist/plan-w-y0limy.d.mts.map +1 -0
- package/dist/sheet-BUnyt5Jt.cjs +576 -0
- package/dist/sheet-DJGhbeYI.mjs +571 -0
- package/dist/vite.cjs +295 -0
- package/dist/vite.d.cts +59 -0
- package/dist/vite.d.cts.map +1 -0
- package/dist/vite.d.mts +59 -0
- package/dist/vite.d.mts.map +1 -0
- package/dist/vite.mjs +290 -0
- package/dist/webpack-loader.cjs +104 -0
- package/dist/webpack-loader.d.cts +35 -0
- package/dist/webpack-loader.d.cts.map +1 -0
- package/dist/webpack-loader.d.mts +36 -0
- package/dist/webpack-loader.d.mts.map +1 -0
- package/dist/webpack-loader.mjs +104 -0
- package/dist/webpack-state-BpF2PPjA.d.cts +15 -0
- package/dist/webpack-state-BpF2PPjA.d.cts.map +1 -0
- package/dist/webpack-state-BtmtzsWZ.cjs +61 -0
- package/dist/webpack-state-CXoQQ7XR.d.mts +15 -0
- package/dist/webpack-state-CXoQQ7XR.d.mts.map +1 -0
- package/dist/webpack-state-DwltSkQq.mjs +44 -0
- package/dist/webpack.cjs +161 -0
- package/dist/webpack.d.cts +98 -0
- package/dist/webpack.d.cts.map +1 -0
- package/dist/webpack.d.mts +98 -0
- package/dist/webpack.d.mts.map +1 -0
- package/dist/webpack.mjs +155 -0
- package/package.json +90 -4
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { t as readSheet } from "./sheet-DJGhbeYI.mjs";
|
|
2
|
+
import MagicString, { Bundle } from "magic-string";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
5
|
+
//#region src/compile.ts
|
|
6
|
+
/**
|
|
7
|
+
* Compiles one owned stylesheet graph into one finished stylesheet: every
|
|
8
|
+
* local `@import` inlined at its position, under its media, supports and
|
|
9
|
+
* layer conditions, and each file's own text placed inside the quarantine
|
|
10
|
+
* layer, or outside it for a file opted out. Each file is decided on its
|
|
11
|
+
* own, before assembly, so a parent's decision never reaches a child's
|
|
12
|
+
* text. File text is kept as written; only relative URLs are rewritten, to
|
|
13
|
+
* resolve from the finished stylesheet's location.
|
|
14
|
+
*/
|
|
15
|
+
const open = (frame) => frame.kind === "media" ? `@media ${frame.text} {\n` : frame.kind === "supports" ? `@supports (${frame.text}) {\n` : `@layer ${frame.text} {\n`;
|
|
16
|
+
/** A URL the finished stylesheet leaves to the browser. */
|
|
17
|
+
const isRemote = (url) => /^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(url);
|
|
18
|
+
/** A URL that resolves the same from any stylesheet location. */
|
|
19
|
+
const isLocationFree = (url) => url === "" || /^(?:[a-z][a-z0-9+.-]*:|\/|#|%23)/i.test(url);
|
|
20
|
+
const quoted = (url) => `"${url.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\a ")}"`;
|
|
21
|
+
/** `@import "x"` names a stylesheet relative to its own; a bare name that
|
|
22
|
+
* is no relative file is the package a bundler resolves. */
|
|
23
|
+
function candidates(url) {
|
|
24
|
+
if (url.startsWith("~")) return [url.slice(1)];
|
|
25
|
+
if (url.startsWith(".") || url.startsWith("/")) return [url];
|
|
26
|
+
return [`./${url}`, url];
|
|
27
|
+
}
|
|
28
|
+
/** Line and column (1-based) of each offset in `text`. */
|
|
29
|
+
function locator(text) {
|
|
30
|
+
const starts = [0];
|
|
31
|
+
for (let at = 0; at < text.length; at++) if (text[at] === "\n") starts.push(at + 1);
|
|
32
|
+
return (offset) => {
|
|
33
|
+
let low = 0;
|
|
34
|
+
let high = starts.length - 1;
|
|
35
|
+
while (low < high) {
|
|
36
|
+
const mid = low + high + 1 >> 1;
|
|
37
|
+
if (starts[mid] <= offset) low = mid;
|
|
38
|
+
else high = mid - 1;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
line: low + 1,
|
|
42
|
+
column: offset - starts[low] + 1
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** A URL's path and its `?query#fragment`. */
|
|
47
|
+
function splitSuffix(url) {
|
|
48
|
+
const at = url.search(/[?#]/);
|
|
49
|
+
return at === -1 ? [url, ""] : [url.slice(0, at), url.slice(at)];
|
|
50
|
+
}
|
|
51
|
+
/** The frames an import adds, media, then supports, then layer; an
|
|
52
|
+
* anonymous layer as `anonymous`. */
|
|
53
|
+
function importFrames(statement, anonymous) {
|
|
54
|
+
const frames = [];
|
|
55
|
+
if (statement.media) frames.push({
|
|
56
|
+
kind: "media",
|
|
57
|
+
text: statement.media
|
|
58
|
+
});
|
|
59
|
+
if (statement.supports !== null) frames.push({
|
|
60
|
+
kind: "supports",
|
|
61
|
+
text: statement.supports
|
|
62
|
+
});
|
|
63
|
+
if (statement.layer.kind === "named") frames.push({
|
|
64
|
+
kind: "layer",
|
|
65
|
+
text: statement.layer.name
|
|
66
|
+
});
|
|
67
|
+
else if (statement.layer.kind === "anonymous") frames.push({
|
|
68
|
+
kind: "layer",
|
|
69
|
+
text: anonymous()
|
|
70
|
+
});
|
|
71
|
+
return frames;
|
|
72
|
+
}
|
|
73
|
+
/** A remote `@import` hoisted to the top of the finished stylesheet: its
|
|
74
|
+
* layer is every layer on its way, its supports every supports, and its
|
|
75
|
+
* media the innermost media. */
|
|
76
|
+
function remoteImport(url, frames) {
|
|
77
|
+
const of = (kind) => frames.filter((frame) => frame.kind === kind).map((frame) => frame.text);
|
|
78
|
+
const [layers, supports, media] = [
|
|
79
|
+
of("layer"),
|
|
80
|
+
of("supports"),
|
|
81
|
+
of("media")
|
|
82
|
+
];
|
|
83
|
+
return [
|
|
84
|
+
`@import url(${quoted(url)})`,
|
|
85
|
+
layers.length > 0 ? ` layer(${layers.join(".")})` : "",
|
|
86
|
+
supports.length > 0 ? ` supports(${supports.map((text) => `(${text})`).join(" and ")})` : "",
|
|
87
|
+
media.length > 0 ? ` ${combinedMedia(media)}` : "",
|
|
88
|
+
";"
|
|
89
|
+
].join("");
|
|
90
|
+
}
|
|
91
|
+
/** Media conditions on the way to an import, as one: joined when each is a
|
|
92
|
+
* bare condition (`(min-width: 1px)`), otherwise the innermost. */
|
|
93
|
+
function combinedMedia(media) {
|
|
94
|
+
return media.every((text) => text.startsWith("(") && !text.includes(",")) ? media.join(" and ") : media.at(-1);
|
|
95
|
+
}
|
|
96
|
+
/** The layer names `frames` nest, outermost first. */
|
|
97
|
+
function layerPath(frames) {
|
|
98
|
+
return frames.filter((frame) => frame.kind === "layer").flatMap((frame) => frame.text.split("."));
|
|
99
|
+
}
|
|
100
|
+
/** Compiles `roots`, in order, into one finished stylesheet. */
|
|
101
|
+
async function compileUnit(roots, options) {
|
|
102
|
+
const deliveryDir = dirname(options.delivery ?? roots[0]);
|
|
103
|
+
/** The URL text reaching `target`; by default relative to the
|
|
104
|
+
* delivery's directory, so a file there keeps its URLs as written. */
|
|
105
|
+
const url = options.url ?? ((target) => relative(deliveryDir, target).split(sep).join("/"));
|
|
106
|
+
/** Files whose own text went into the layer, and owned files opted out. */
|
|
107
|
+
const layered = /* @__PURE__ */ new Set();
|
|
108
|
+
const optedOut = /* @__PURE__ */ new Set();
|
|
109
|
+
const bundle = new Bundle({ separator: "" });
|
|
110
|
+
const diagnostics = [];
|
|
111
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
112
|
+
const remote = [];
|
|
113
|
+
/** Every layer path, in the order the original cascade first creates it. */
|
|
114
|
+
const created = /* @__PURE__ */ new Set();
|
|
115
|
+
const create = (path) => {
|
|
116
|
+
for (let depth = 1; depth <= path.length; depth++) {
|
|
117
|
+
const name = path.slice(0, depth).join(".");
|
|
118
|
+
created.add(name);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const report = (code, severity, message, file, at) => {
|
|
122
|
+
diagnostics.push({
|
|
123
|
+
code,
|
|
124
|
+
severity,
|
|
125
|
+
message,
|
|
126
|
+
file,
|
|
127
|
+
...loaded.get(file).locate(at)
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
/** A stable private name for the anonymous layer an import occurrence
|
|
131
|
+
* creates: from the unit and the chain of imports that reaches it from
|
|
132
|
+
* the root, so each occurrence, of the same file too, gets its own, and
|
|
133
|
+
* the same on every machine. */
|
|
134
|
+
const anonymousName = (occurrence) => () => `layergate-anonymous-${createHash("sha256").update(occurrence).digest("hex").slice(0, 10)}`;
|
|
135
|
+
/** What a file holds that the layer cannot hold back, or that layergate
|
|
136
|
+
* cannot place, reported once per file. */
|
|
137
|
+
const reportEffects = (file, sheet) => {
|
|
138
|
+
const effect = (offsets, code, what) => {
|
|
139
|
+
if (offsets.length === 0) return;
|
|
140
|
+
const them = offsets.length === 1 ? "it" : "them";
|
|
141
|
+
report(code, "warning", `${offsets.length} ${what} kept: the quarantine layer does not hold ${them} back`, file, offsets[0]);
|
|
142
|
+
};
|
|
143
|
+
effect(sheet.important, "layergate.important", "!important declaration(s)");
|
|
144
|
+
effect(sheet.keyframes, "layergate.animation", "@keyframes rule(s)");
|
|
145
|
+
effect(sheet.animations, "layergate.animation", "animation declaration(s)");
|
|
146
|
+
};
|
|
147
|
+
/** What layergate leaves as written or cannot place, reported once per
|
|
148
|
+
* file; `placed` says the file's text sits inside a block, where
|
|
149
|
+
* `@namespace` cannot. */
|
|
150
|
+
const reportFile = (file, sheet, placed) => {
|
|
151
|
+
for (const at of sheet.ignoredImports) report("layergate.ignored-import", "warning", "@import after other rules is ignored by browsers, and layergate leaves it as written", file, at);
|
|
152
|
+
if (placed && sheet.namespaces.length > 0) report("layergate.namespace", "error", "@namespace applies to a whole stylesheet, which layergate cannot place yet", file, sheet.namespaces[0]);
|
|
153
|
+
};
|
|
154
|
+
const load = async (file) => {
|
|
155
|
+
if (loaded.has(file)) return loaded.get(file);
|
|
156
|
+
let entry = null;
|
|
157
|
+
try {
|
|
158
|
+
let parsed = options.cache?.get(file);
|
|
159
|
+
if (!parsed) {
|
|
160
|
+
const text = String(await options.read(file));
|
|
161
|
+
parsed = {
|
|
162
|
+
text,
|
|
163
|
+
sheet: readSheet(text)
|
|
164
|
+
};
|
|
165
|
+
options.cache?.set(file, parsed);
|
|
166
|
+
}
|
|
167
|
+
const source = new MagicString(parsed.text);
|
|
168
|
+
const kept = !options.url && dirname(file) === deliveryDir;
|
|
169
|
+
for (const reference of kept ? [] : parsed.sheet.urls) {
|
|
170
|
+
if (isLocationFree(reference.url) || isRemote(reference.url)) continue;
|
|
171
|
+
const [path, suffix] = splitSuffix(reference.url);
|
|
172
|
+
const rebased = url(join(dirname(file), path)) + suffix;
|
|
173
|
+
if (rebased === reference.url) continue;
|
|
174
|
+
source.overwrite(reference.start, reference.end, reference.kind === "token" ? `url(${quoted(rebased)})` : quoted(rebased));
|
|
175
|
+
}
|
|
176
|
+
for (const [from, to] of parsed.sheet.removed) source.remove(from, to);
|
|
177
|
+
entry = {
|
|
178
|
+
...parsed,
|
|
179
|
+
source,
|
|
180
|
+
locate: locator(parsed.text)
|
|
181
|
+
};
|
|
182
|
+
} catch (error) {
|
|
183
|
+
diagnostics.push({
|
|
184
|
+
code: "layergate.unreadable",
|
|
185
|
+
severity: "error",
|
|
186
|
+
message: `cannot read ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
187
|
+
file
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
loaded.set(file, entry);
|
|
191
|
+
return entry;
|
|
192
|
+
};
|
|
193
|
+
/** The file's text from `from` to `to` inside `frames`, unless it holds
|
|
194
|
+
* only whitespace. */
|
|
195
|
+
const emit = (file, frames, from, to) => {
|
|
196
|
+
const entry = loaded.get(file);
|
|
197
|
+
let content = false;
|
|
198
|
+
let at = from;
|
|
199
|
+
for (const [start, end] of entry.sheet.removed) {
|
|
200
|
+
if (end <= at || start >= to) continue;
|
|
201
|
+
content ||= /\S/.test(entry.text.slice(at, start));
|
|
202
|
+
at = end;
|
|
203
|
+
}
|
|
204
|
+
if (!content && !/\S/.test(entry.text.slice(at, to))) return;
|
|
205
|
+
if (frames.length > 0) bundle.addSource({ content: new MagicString(frames.map(open).join("")) });
|
|
206
|
+
bundle.addSource({
|
|
207
|
+
filename: file,
|
|
208
|
+
content: entry.source.snip(from, to)
|
|
209
|
+
});
|
|
210
|
+
bundle.addSource({ content: new MagicString(`\n${"}\n".repeat(frames.length)}`) });
|
|
211
|
+
};
|
|
212
|
+
/** Emits `file` as one occurrence: `frames` are the conditions its import
|
|
213
|
+
* chain puts it under, `stack` the files on that chain, `occurrence` the
|
|
214
|
+
* chain's import indexes. A file imported twice is emitted at each
|
|
215
|
+
* import, as a browser applies it at each. */
|
|
216
|
+
const visit = async (file, frames, stack, occurrence, insideOwned) => {
|
|
217
|
+
const first = !loaded.has(file);
|
|
218
|
+
const entry = await load(file);
|
|
219
|
+
if (!entry) return;
|
|
220
|
+
const owned = insideOwned || (options.owns?.(file) ?? true);
|
|
221
|
+
const quarantined = owned && (options.quarantined?.(file) ?? true);
|
|
222
|
+
if (owned && !quarantined) optedOut.add(file);
|
|
223
|
+
if (first) reportFile(file, entry.sheet, quarantined || stack.length > 1);
|
|
224
|
+
if (quarantined && !layered.has(file)) {
|
|
225
|
+
layered.add(file);
|
|
226
|
+
reportEffects(file, entry.sheet);
|
|
227
|
+
}
|
|
228
|
+
const own = quarantined ? [{
|
|
229
|
+
kind: "layer",
|
|
230
|
+
text: options.layer
|
|
231
|
+
}, ...frames] : frames;
|
|
232
|
+
const ownPath = layerPath(own);
|
|
233
|
+
const createLayers = (from, to) => {
|
|
234
|
+
for (const { at, names } of entry.sheet.layers) if (at >= from && at < to) for (const name of names) create([...ownPath, ...name.split(".")]);
|
|
235
|
+
};
|
|
236
|
+
create(ownPath);
|
|
237
|
+
let cursor = 0;
|
|
238
|
+
for (const [index, statement] of entry.sheet.imports.entries()) {
|
|
239
|
+
createLayers(cursor, statement.start);
|
|
240
|
+
emit(file, own, cursor, statement.start);
|
|
241
|
+
cursor = statement.end;
|
|
242
|
+
const added = importFrames(statement, anonymousName(`${occurrence}/${index}`));
|
|
243
|
+
if (isRemote(statement.url)) {
|
|
244
|
+
const chain = [...own, ...added];
|
|
245
|
+
if (options.remote === "refuse" && chain.some((frame) => frame.kind !== "media")) {
|
|
246
|
+
report("layergate.remote", "error", `@import "${statement.url}" is remote, and this host cannot load it inside a layer: vendor the sheet locally, or opt this file out explicitly if unlayered is acceptable`, file, statement.start);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const media = chain.filter((frame) => frame.kind === "media").length;
|
|
250
|
+
const kept = media > 1 ? `; its ${media} media conditions are joined where each is a bare condition, else only the innermost is kept` : "";
|
|
251
|
+
report("layergate.remote", "warning", `@import "${statement.url}" stays remote, so its contents are not inspected${kept}`, file, statement.start);
|
|
252
|
+
create(layerPath(chain));
|
|
253
|
+
remote.push(remoteImport(statement.url, chain));
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
let child = null;
|
|
257
|
+
for (const candidate of candidates(statement.url)) {
|
|
258
|
+
child = await options.resolve(candidate, file);
|
|
259
|
+
if (child) break;
|
|
260
|
+
}
|
|
261
|
+
if (!child) report("layergate.unresolved", "error", `cannot resolve @import "${statement.url}"`, file, statement.start);
|
|
262
|
+
else if (stack.includes(child)) report("layergate.cycle", "warning", `@import "${statement.url}" imports a stylesheet already being imported; skipped`, file, statement.start);
|
|
263
|
+
else await visit(child, [...frames, ...added], [...stack, child], `${occurrence}/${index}`, owned);
|
|
264
|
+
}
|
|
265
|
+
createLayers(cursor, entry.text.length);
|
|
266
|
+
emit(file, own, cursor, entry.text.length);
|
|
267
|
+
};
|
|
268
|
+
for (const root of roots) {
|
|
269
|
+
const name = relative(options.base ?? deliveryDir, root).split(sep).join("/");
|
|
270
|
+
await visit(root, [], [root], name, false);
|
|
271
|
+
}
|
|
272
|
+
bundle.prepend([
|
|
273
|
+
`@layer ${options.order.join(", ")};\n`,
|
|
274
|
+
...remote.length > 0 ? [`@layer ${[...created].join(", ")};\n`] : [],
|
|
275
|
+
...remote.map((line) => `${line}\n`)
|
|
276
|
+
].join(""));
|
|
277
|
+
for (const file of optedOut) diagnostics.push({
|
|
278
|
+
code: "layergate.opt-out",
|
|
279
|
+
severity: "info",
|
|
280
|
+
message: `opted out: its own rules stay outside @layer ${options.layer}`,
|
|
281
|
+
file
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
css: bundle.toString(),
|
|
285
|
+
map: options.map === false ? null : bundle.generateMap({
|
|
286
|
+
hires: "boundary",
|
|
287
|
+
includeContent: true
|
|
288
|
+
}),
|
|
289
|
+
files: [...loaded.keys()],
|
|
290
|
+
layered: layered.size > 0,
|
|
291
|
+
diagnostics
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
295
|
+
export { compileUnit as t };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Bundle } from "magic-string";
|
|
2
|
+
//#region src/sheet.d.ts
|
|
3
|
+
/** The layer an `@import` puts its stylesheet in. */
|
|
4
|
+
type ImportLayer = {
|
|
5
|
+
kind: 'none';
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'anonymous';
|
|
8
|
+
} |
|
|
9
|
+
/** `layer(name)`: the name as written. */
|
|
10
|
+
{
|
|
11
|
+
kind: 'named';
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
interface ImportStatement {
|
|
15
|
+
/** The statement's span, through its `;`. */
|
|
16
|
+
start: number;
|
|
17
|
+
end: number;
|
|
18
|
+
/** The decoded URL. */
|
|
19
|
+
url: string;
|
|
20
|
+
layer: ImportLayer;
|
|
21
|
+
/** The text inside `supports(…)`, as written, or null. */
|
|
22
|
+
supports: string | null;
|
|
23
|
+
/** The media query list as written, or `''`. */
|
|
24
|
+
media: string;
|
|
25
|
+
}
|
|
26
|
+
/** A URL reference: the span to replace and the decoded URL. `token`
|
|
27
|
+
* replaces a whole `url(…)` token, `string` only a string argument
|
|
28
|
+
* (`url("x")`, `image-set("x" 1x)`). */
|
|
29
|
+
interface UrlReference {
|
|
30
|
+
start: number;
|
|
31
|
+
end: number;
|
|
32
|
+
url: string;
|
|
33
|
+
kind: 'token' | 'string';
|
|
34
|
+
}
|
|
35
|
+
interface Sheet {
|
|
36
|
+
imports: ImportStatement[];
|
|
37
|
+
/** Spans of the leading statements that are not the file's own content:
|
|
38
|
+
* `@charset` and `@import`. */
|
|
39
|
+
removed: Array<[number, number]>;
|
|
40
|
+
/** Offsets of the `@namespace` statements. */
|
|
41
|
+
namespaces: number[];
|
|
42
|
+
urls: UrlReference[];
|
|
43
|
+
/** Offsets of `!important`, `@keyframes` and `animation` declarations. */
|
|
44
|
+
important: number[];
|
|
45
|
+
keyframes: number[];
|
|
46
|
+
animations: number[];
|
|
47
|
+
/** Offsets of `@import` rules past the leading statements, which a
|
|
48
|
+
* browser ignores. */
|
|
49
|
+
ignoredImports: number[];
|
|
50
|
+
/** The layer names each top-level `@layer` rule creates, as written, by
|
|
51
|
+
* offset; an anonymous block names none. */
|
|
52
|
+
layers: Array<{
|
|
53
|
+
at: number;
|
|
54
|
+
names: string[];
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/compile.d.ts
|
|
59
|
+
type Severity = 'error' | 'warning' | 'info';
|
|
60
|
+
interface LayergateDiagnostic {
|
|
61
|
+
code: string;
|
|
62
|
+
severity: Severity;
|
|
63
|
+
message: string;
|
|
64
|
+
file?: string;
|
|
65
|
+
line?: number;
|
|
66
|
+
column?: number;
|
|
67
|
+
}
|
|
68
|
+
/** A file's text and structure, which a host may keep between compiles
|
|
69
|
+
* until the file changes. */
|
|
70
|
+
interface ParsedSheet {
|
|
71
|
+
text: string;
|
|
72
|
+
sheet: Sheet;
|
|
73
|
+
}
|
|
74
|
+
interface CompileOptions {
|
|
75
|
+
/** The quarantine layer each owned file's own text goes into. */
|
|
76
|
+
layer: string;
|
|
77
|
+
/** The full layer order the finished stylesheet declares first. */
|
|
78
|
+
order: readonly string[];
|
|
79
|
+
/** The host's CSS resolver: the absolute file `specifier` reaches from
|
|
80
|
+
* `importer`, or null. */
|
|
81
|
+
resolve(specifier: string, importer: string): Promise<string | null> | string | null;
|
|
82
|
+
read(file: string): Promise<string> | string;
|
|
83
|
+
/** Whether `file`, inside an owned graph, has its own text in the
|
|
84
|
+
* quarantine layer; false for a file opted out, which still loads,
|
|
85
|
+
* outside it. Default: every owned file is. */
|
|
86
|
+
quarantined?(file: string): boolean;
|
|
87
|
+
/** Whether `file` starts an owned graph; every file it reaches is owned
|
|
88
|
+
* too. A file outside every owned graph keeps its own text unlayered.
|
|
89
|
+
* Default: every file is owned. */
|
|
90
|
+
owns?(file: string): boolean;
|
|
91
|
+
/** The file the finished stylesheet is delivered as. Default: the
|
|
92
|
+
* first root. */
|
|
93
|
+
delivery?: string;
|
|
94
|
+
/** The URL text that reaches the absolute file `target` from the finished
|
|
95
|
+
* stylesheet. Default: relative to the delivery's directory, and a file
|
|
96
|
+
* in that directory keeps its URLs as written. */
|
|
97
|
+
url?(target: string): string;
|
|
98
|
+
/** The directory generated names are relative to, so they are the same
|
|
99
|
+
* on every machine: the project root. Default: the first root's. */
|
|
100
|
+
base?: string;
|
|
101
|
+
/** What happens to a remote `@import`: `hoist` (default) keeps it
|
|
102
|
+
* remote, hoisted ahead of every rule in its layer; `refuse` reports an
|
|
103
|
+
* error instead wherever it would need `layer()` or `supports()`, for a
|
|
104
|
+
* host whose CSS pipeline cannot carry them (Next's webpack css-loader),
|
|
105
|
+
* and hoists only a plain or media-only one. */
|
|
106
|
+
remote?: 'hoist' | 'refuse';
|
|
107
|
+
/** Whether to compose a source map. Default true. */
|
|
108
|
+
map?: boolean;
|
|
109
|
+
/** Parsed files by path, read and filled by the compile. */
|
|
110
|
+
cache?: Map<string, ParsedSheet>;
|
|
111
|
+
}
|
|
112
|
+
interface CompiledUnit {
|
|
113
|
+
css: string;
|
|
114
|
+
/** Null when `map` is false. */
|
|
115
|
+
map: ReturnType<Bundle['generateMap']> | null;
|
|
116
|
+
/** Every file read, the roots first. */
|
|
117
|
+
files: string[];
|
|
118
|
+
/** Whether any file's own text went into the quarantine layer. */
|
|
119
|
+
layered: boolean;
|
|
120
|
+
diagnostics: LayergateDiagnostic[];
|
|
121
|
+
}
|
|
122
|
+
/** Compiles `roots`, in order, into one finished stylesheet. */
|
|
123
|
+
declare function compileUnit(roots: readonly string[], options: CompileOptions): Promise<CompiledUnit>;
|
|
124
|
+
//#endregion
|
|
125
|
+
export { Severity as a, ParsedSheet as i, CompiledUnit as n, compileUnit as o, LayergateDiagnostic as r, CompileOptions as t };
|
|
126
|
+
//# sourceMappingURL=compile-DZwVBjTM.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-DZwVBjTM.d.cts","names":[],"sources":["../src/sheet.ts","../src/compile.ts"],"mappings":";;;KAUY;EACN;;EACA;;;;EAEA;EAAe;;UAEJ;;EAEf;EACA;;EAEA;EACA,OAAO;;EAEP;;EAEA;;;;;UAMe;EACf;EACA;EACA;EACA;;UAGe;EACf,SAAS;;;EAGT,SAAS;;EAET;EACA,MAAM;;EAEN;EACA;EACA;;;EAGA;;;EAGA,QAAQ;IAAQ;IAAY;;;;;KCvClB;UAEK;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;;;;UAKe;EACf;EACA,OAAO;;UAGQ;;EAEf;;EAEA;;;EAGA,QACE,mBACA,mBACC;EACH,KAAK,eAAe;;;;EAIpB,aAAa;;;;EAIb,MAAM;;;EAGN;;;;EAIA,KAAK;;;EAGL;;;;;;EAMA;;EAEA;;EAEA,QAAQ,YAAY;;UAGL;EACf;;EAEA,KAAK,WAAW;;EAEhB;;EAEA;EACA,aAAa;;;iBA4GO,YACpB,0BACA,SAAS,iBACR,QAAQ"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Bundle } from "magic-string";
|
|
2
|
+
//#region src/sheet.d.ts
|
|
3
|
+
/** The layer an `@import` puts its stylesheet in. */
|
|
4
|
+
type ImportLayer = {
|
|
5
|
+
kind: 'none';
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'anonymous';
|
|
8
|
+
} |
|
|
9
|
+
/** `layer(name)`: the name as written. */
|
|
10
|
+
{
|
|
11
|
+
kind: 'named';
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
interface ImportStatement {
|
|
15
|
+
/** The statement's span, through its `;`. */
|
|
16
|
+
start: number;
|
|
17
|
+
end: number;
|
|
18
|
+
/** The decoded URL. */
|
|
19
|
+
url: string;
|
|
20
|
+
layer: ImportLayer;
|
|
21
|
+
/** The text inside `supports(…)`, as written, or null. */
|
|
22
|
+
supports: string | null;
|
|
23
|
+
/** The media query list as written, or `''`. */
|
|
24
|
+
media: string;
|
|
25
|
+
}
|
|
26
|
+
/** A URL reference: the span to replace and the decoded URL. `token`
|
|
27
|
+
* replaces a whole `url(…)` token, `string` only a string argument
|
|
28
|
+
* (`url("x")`, `image-set("x" 1x)`). */
|
|
29
|
+
interface UrlReference {
|
|
30
|
+
start: number;
|
|
31
|
+
end: number;
|
|
32
|
+
url: string;
|
|
33
|
+
kind: 'token' | 'string';
|
|
34
|
+
}
|
|
35
|
+
interface Sheet {
|
|
36
|
+
imports: ImportStatement[];
|
|
37
|
+
/** Spans of the leading statements that are not the file's own content:
|
|
38
|
+
* `@charset` and `@import`. */
|
|
39
|
+
removed: Array<[number, number]>;
|
|
40
|
+
/** Offsets of the `@namespace` statements. */
|
|
41
|
+
namespaces: number[];
|
|
42
|
+
urls: UrlReference[];
|
|
43
|
+
/** Offsets of `!important`, `@keyframes` and `animation` declarations. */
|
|
44
|
+
important: number[];
|
|
45
|
+
keyframes: number[];
|
|
46
|
+
animations: number[];
|
|
47
|
+
/** Offsets of `@import` rules past the leading statements, which a
|
|
48
|
+
* browser ignores. */
|
|
49
|
+
ignoredImports: number[];
|
|
50
|
+
/** The layer names each top-level `@layer` rule creates, as written, by
|
|
51
|
+
* offset; an anonymous block names none. */
|
|
52
|
+
layers: Array<{
|
|
53
|
+
at: number;
|
|
54
|
+
names: string[];
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/compile.d.ts
|
|
59
|
+
type Severity = 'error' | 'warning' | 'info';
|
|
60
|
+
interface LayergateDiagnostic {
|
|
61
|
+
code: string;
|
|
62
|
+
severity: Severity;
|
|
63
|
+
message: string;
|
|
64
|
+
file?: string;
|
|
65
|
+
line?: number;
|
|
66
|
+
column?: number;
|
|
67
|
+
}
|
|
68
|
+
/** A file's text and structure, which a host may keep between compiles
|
|
69
|
+
* until the file changes. */
|
|
70
|
+
interface ParsedSheet {
|
|
71
|
+
text: string;
|
|
72
|
+
sheet: Sheet;
|
|
73
|
+
}
|
|
74
|
+
interface CompileOptions {
|
|
75
|
+
/** The quarantine layer each owned file's own text goes into. */
|
|
76
|
+
layer: string;
|
|
77
|
+
/** The full layer order the finished stylesheet declares first. */
|
|
78
|
+
order: readonly string[];
|
|
79
|
+
/** The host's CSS resolver: the absolute file `specifier` reaches from
|
|
80
|
+
* `importer`, or null. */
|
|
81
|
+
resolve(specifier: string, importer: string): Promise<string | null> | string | null;
|
|
82
|
+
read(file: string): Promise<string> | string;
|
|
83
|
+
/** Whether `file`, inside an owned graph, has its own text in the
|
|
84
|
+
* quarantine layer; false for a file opted out, which still loads,
|
|
85
|
+
* outside it. Default: every owned file is. */
|
|
86
|
+
quarantined?(file: string): boolean;
|
|
87
|
+
/** Whether `file` starts an owned graph; every file it reaches is owned
|
|
88
|
+
* too. A file outside every owned graph keeps its own text unlayered.
|
|
89
|
+
* Default: every file is owned. */
|
|
90
|
+
owns?(file: string): boolean;
|
|
91
|
+
/** The file the finished stylesheet is delivered as. Default: the
|
|
92
|
+
* first root. */
|
|
93
|
+
delivery?: string;
|
|
94
|
+
/** The URL text that reaches the absolute file `target` from the finished
|
|
95
|
+
* stylesheet. Default: relative to the delivery's directory, and a file
|
|
96
|
+
* in that directory keeps its URLs as written. */
|
|
97
|
+
url?(target: string): string;
|
|
98
|
+
/** The directory generated names are relative to, so they are the same
|
|
99
|
+
* on every machine: the project root. Default: the first root's. */
|
|
100
|
+
base?: string;
|
|
101
|
+
/** What happens to a remote `@import`: `hoist` (default) keeps it
|
|
102
|
+
* remote, hoisted ahead of every rule in its layer; `refuse` reports an
|
|
103
|
+
* error instead wherever it would need `layer()` or `supports()`, for a
|
|
104
|
+
* host whose CSS pipeline cannot carry them (Next's webpack css-loader),
|
|
105
|
+
* and hoists only a plain or media-only one. */
|
|
106
|
+
remote?: 'hoist' | 'refuse';
|
|
107
|
+
/** Whether to compose a source map. Default true. */
|
|
108
|
+
map?: boolean;
|
|
109
|
+
/** Parsed files by path, read and filled by the compile. */
|
|
110
|
+
cache?: Map<string, ParsedSheet>;
|
|
111
|
+
}
|
|
112
|
+
interface CompiledUnit {
|
|
113
|
+
css: string;
|
|
114
|
+
/** Null when `map` is false. */
|
|
115
|
+
map: ReturnType<Bundle['generateMap']> | null;
|
|
116
|
+
/** Every file read, the roots first. */
|
|
117
|
+
files: string[];
|
|
118
|
+
/** Whether any file's own text went into the quarantine layer. */
|
|
119
|
+
layered: boolean;
|
|
120
|
+
diagnostics: LayergateDiagnostic[];
|
|
121
|
+
}
|
|
122
|
+
/** Compiles `roots`, in order, into one finished stylesheet. */
|
|
123
|
+
declare function compileUnit(roots: readonly string[], options: CompileOptions): Promise<CompiledUnit>;
|
|
124
|
+
//#endregion
|
|
125
|
+
export { Severity as a, ParsedSheet as i, CompiledUnit as n, compileUnit as o, LayergateDiagnostic as r, CompileOptions as t };
|
|
126
|
+
//# sourceMappingURL=compile-DZwVBjTM.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-DZwVBjTM.d.mts","names":[],"sources":["../src/sheet.ts","../src/compile.ts"],"mappings":";;;KAUY;EACN;;EACA;;;;EAEA;EAAe;;UAEJ;;EAEf;EACA;;EAEA;EACA,OAAO;;EAEP;;EAEA;;;;;UAMe;EACf;EACA;EACA;EACA;;UAGe;EACf,SAAS;;;EAGT,SAAS;;EAET;EACA,MAAM;;EAEN;EACA;EACA;;;EAGA;;;EAGA,QAAQ;IAAQ;IAAY;;;;;KCvClB;UAEK;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;;;;UAKe;EACf;EACA,OAAO;;UAGQ;;EAEf;;EAEA;;;EAGA,QACE,mBACA,mBACC;EACH,KAAK,eAAe;;;;EAIpB,aAAa;;;;EAIb,MAAM;;;EAGN;;;;EAIA,KAAK;;;EAGL;;;;;;EAMA;;EAEA;;EAEA,QAAQ,YAAY;;UAGL;EACf;;EAEA,KAAK,WAAW;;EAEhB;;EAEA;EACA,aAAa;;;iBA4GO,YACpB,0BACA,SAAS,iBACR,QAAQ"}
|