@master/css-tooling 0.0.0-site.1793659883.g69c9aebcf628

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.
Files changed (122) hide show
  1. package/README.md +58 -0
  2. package/dist/browser.d.ts +2 -0
  3. package/dist/browser.js +1 -0
  4. package/dist/css-tree-version.d.ts +2 -0
  5. package/dist/css-tree-version.js +4 -0
  6. package/dist/css.d.ts +32 -0
  7. package/dist/css.js +120 -0
  8. package/dist/immutable.d.ts +2 -0
  9. package/dist/immutable.js +7 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +1 -0
  12. package/dist/language/binding-session.d.ts +6 -0
  13. package/dist/language/binding-session.js +8 -0
  14. package/dist/language/browser.d.ts +10 -0
  15. package/dist/language/browser.js +12 -0
  16. package/dist/language/common.d.ts +12 -0
  17. package/dist/language/common.js +55 -0
  18. package/dist/language/contracts.d.ts +197 -0
  19. package/dist/language/contracts.js +1 -0
  20. package/dist/language/index.d.ts +29 -0
  21. package/dist/language/index.js +39 -0
  22. package/dist/language/master-css.d.ts +13 -0
  23. package/dist/language/master-css.js +35 -0
  24. package/dist/language/node.d.ts +21 -0
  25. package/dist/language/node.js +36 -0
  26. package/dist/language/semantic/scopes.d.ts +55 -0
  27. package/dist/language/semantic/scopes.js +73 -0
  28. package/dist/language/semantic/types.d.ts +11 -0
  29. package/dist/language/semantic/types.js +1 -0
  30. package/dist/language/session.d.ts +37 -0
  31. package/dist/language/session.js +87 -0
  32. package/dist/language/settings.d.ts +13 -0
  33. package/dist/language/settings.js +19 -0
  34. package/dist/language/utils/mdn-css-data.d.ts +5 -0
  35. package/dist/language/utils/mdn-css-data.js +63 -0
  36. package/dist/lexer/browser.d.ts +6 -0
  37. package/dist/lexer/browser.js +11 -0
  38. package/dist/lexer/contracts.d.ts +41 -0
  39. package/dist/lexer/contracts.js +1 -0
  40. package/dist/lexer/index.d.ts +8 -0
  41. package/dist/lexer/index.js +11 -0
  42. package/dist/lexer/node.d.ts +7 -0
  43. package/dist/lexer/node.js +11 -0
  44. package/dist/lexer/session.d.ts +17 -0
  45. package/dist/lexer/session.js +25 -0
  46. package/dist/lint/analysis.d.ts +90 -0
  47. package/dist/lint/analysis.js +1 -0
  48. package/dist/lint/binding-session.d.ts +21 -0
  49. package/dist/lint/binding-session.js +97 -0
  50. package/dist/lint/constants.d.ts +7 -0
  51. package/dist/lint/constants.js +18 -0
  52. package/dist/lint/contracts.d.ts +17 -0
  53. package/dist/lint/contracts.js +6 -0
  54. package/dist/lint/diagnostic-format.d.ts +5 -0
  55. package/dist/lint/diagnostic-format.js +15 -0
  56. package/dist/lint/diagnostics.d.ts +42 -0
  57. package/dist/lint/diagnostics.js +1 -0
  58. package/dist/lint/index.d.ts +21 -0
  59. package/dist/lint/index.js +30 -0
  60. package/dist/lint/node.d.ts +6 -0
  61. package/dist/lint/node.js +11 -0
  62. package/dist/lint/rule-signatures.d.ts +23 -0
  63. package/dist/lint/rule-signatures.js +27 -0
  64. package/dist/lint/source.d.ts +70 -0
  65. package/dist/lint/source.js +261 -0
  66. package/dist/math-types.d.ts +11 -0
  67. package/dist/math-types.js +236 -0
  68. package/dist/math-validation.d.ts +7 -0
  69. package/dist/math-validation.js +74 -0
  70. package/dist/node.d.ts +12 -0
  71. package/dist/node.js +32 -0
  72. package/dist/scanner/binding-session.d.ts +29 -0
  73. package/dist/scanner/binding-session.js +37 -0
  74. package/dist/scanner/core.d.ts +130 -0
  75. package/dist/scanner/core.js +480 -0
  76. package/dist/scanner/index.d.ts +3 -0
  77. package/dist/scanner/index.js +3 -0
  78. package/dist/scanner/node.d.ts +5 -0
  79. package/dist/scanner/node.js +4 -0
  80. package/dist/scanner/options/index.d.ts +16 -0
  81. package/dist/scanner/options/index.js +57 -0
  82. package/dist/scanner/source-policy.d.ts +15 -0
  83. package/dist/scanner/source-policy.js +131 -0
  84. package/dist/source/adapters/optional-peer.d.ts +5 -0
  85. package/dist/source/adapters/optional-peer.js +29 -0
  86. package/dist/source/adapters/oxc.d.ts +2 -0
  87. package/dist/source/adapters/oxc.js +6 -0
  88. package/dist/source/adapters/svelte.d.ts +5 -0
  89. package/dist/source/adapters/svelte.js +82 -0
  90. package/dist/source/adapters/types.d.ts +11 -0
  91. package/dist/source/adapters/types.js +9 -0
  92. package/dist/source/adapters/vue.d.ts +5 -0
  93. package/dist/source/adapters/vue.js +50 -0
  94. package/dist/source/browser.d.ts +7 -0
  95. package/dist/source/browser.js +11 -0
  96. package/dist/source/contracts.d.ts +35 -0
  97. package/dist/source/contracts.js +1 -0
  98. package/dist/source/index.d.ts +8 -0
  99. package/dist/source/index.js +11 -0
  100. package/dist/source/native.d.ts +5 -0
  101. package/dist/source/native.js +53 -0
  102. package/dist/source/node.d.ts +7 -0
  103. package/dist/source/node.js +11 -0
  104. package/dist/source/session.d.ts +25 -0
  105. package/dist/source/session.js +37 -0
  106. package/dist/syntax-validation.d.ts +10 -0
  107. package/dist/syntax-validation.js +54 -0
  108. package/dist/tooling-session.d.ts +66 -0
  109. package/dist/tooling-session.js +188 -0
  110. package/dist/validator/binding-session.d.ts +17 -0
  111. package/dist/validator/binding-session.js +29 -0
  112. package/dist/validator/contracts.d.ts +24 -0
  113. package/dist/validator/contracts.js +1 -0
  114. package/dist/validator/index.d.ts +10 -0
  115. package/dist/validator/index.js +13 -0
  116. package/dist/validator/node.d.ts +6 -0
  117. package/dist/validator/node.js +11 -0
  118. package/dist/validator/types/syntax-error.d.ts +22 -0
  119. package/dist/validator/types/syntax-error.js +1 -0
  120. package/dist/value-validation.d.ts +46 -0
  121. package/dist/value-validation.js +162 -0
  122. package/package.json +127 -0
@@ -0,0 +1,37 @@
1
+ import { createToolingBindingSync } from "@master/css-binding/tooling/node";
2
+ import { createToolingBinding } from "@master/css-binding/tooling";
3
+ // src/scanner/binding-session.ts
4
+ function serializeScannerBlocklist(blocklist = []) {
5
+ return [...blocklist].map((entry) => typeof entry === "string" ? entry : {
6
+ source: entry.source,
7
+ flags: entry.flags
8
+ });
9
+ }
10
+ function bindScannerSession(binding, session) {
11
+ return {
12
+ binding,
13
+ extractCandidates: (source, content, options) => [...session.extractCandidates(source, content, options)],
14
+ collectCandidates: (candidates) => [...session.collectCandidates(candidates)],
15
+ filterCandidates: (candidates, blocklist) => [...session.filterCandidates(candidates, blocklist)],
16
+ scanCandidates: (source, content, candidates, blocklist, options) => session.scanCandidates(source, content, candidates, blocklist, options),
17
+ removeSource: (source, options) => session.removeSource(source, options),
18
+ reconcileSources: (owner, inputs) => session.reconcileSources(owner, inputs),
19
+ removeOwner: (owner) => session.removeOwner(owner),
20
+ ensureClasses: (classes) => {
21
+ session.ensureClassRules(classes);
22
+ },
23
+ registerNativeClasses: (owner, classes) => session.registerNativeClassNames(owner, classes),
24
+ reset: () => session.reset(),
25
+ state: () => session.snapshot(),
26
+ dispose: () => session.dispose()
27
+ };
28
+ }
29
+ function createNativeScannerSession(manifest) {
30
+ const tooling = createToolingBindingSync();
31
+ return bindScannerSession(tooling.binding, tooling.createScannerSession(manifest));
32
+ }
33
+ async function createScannerSession(manifest, options = {}) {
34
+ const tooling = await createToolingBinding(options);
35
+ return bindScannerSession(tooling.binding, await tooling.createScannerSession(manifest));
36
+ }
37
+ export { createNativeScannerSession, createScannerSession, serializeScannerBlocklist };
@@ -0,0 +1,130 @@
1
+ import { ScannerSourceInput, ScannerSourceOptions } from "./binding-session.js";
2
+ import { MasterCSSScannerOptions } from "./options/index.js";
3
+ import { EventEmitter } from "node:events";
4
+ import { MasterCSSManifest } from "@master/css-schema/manifest";
5
+ interface ScannerResetOptions {
6
+ emit?: boolean;
7
+ }
8
+ interface MasterCSSScannerSourceResult {
9
+ readonly changed: boolean;
10
+ readonly sourceChanged: boolean;
11
+ /** All candidates extracted from this input, including previously scanned classes. */
12
+ readonly candidates: readonly string[];
13
+ }
14
+ declare class ScannerCSSView {
15
+ readonly scanner: MasterCSSScanner;
16
+ constructor(scanner: MasterCSSScanner);
17
+ get manifest(): MasterCSSManifest;
18
+ get text(): string;
19
+ get utilitiesLayer(): {
20
+ rules: {
21
+ className: string;
22
+ key: string;
23
+ layer: import("@master/css-schema/manifest").MasterCSSManifestUtilityLayerName;
24
+ type: import("~/packages/schema/src/utility-type").UtilityType;
25
+ sortTier: number;
26
+ priority: import("~/packages/schema/src/hydration-manifest").MasterCSSRulePriority;
27
+ text: string;
28
+ nodes?: readonly import("~/packages/schema/src/hydration-manifest").MasterCSSHydrationRuleNode[];
29
+ selectorText?: string;
30
+ variableNames?: readonly string[];
31
+ keyframeNames?: readonly string[];
32
+ retainAllKeyframes?: boolean;
33
+ name: string;
34
+ }[];
35
+ };
36
+ }
37
+ declare class ScannerStateSetView {
38
+ private readonly readValues;
39
+ readonly [Symbol.toStringTag] = "ScannerStateSetView";
40
+ constructor(readValues: () => readonly string[]);
41
+ get size(): number;
42
+ has(value: string): boolean;
43
+ entries(): SetIterator<[string, string]>;
44
+ keys(): SetIterator<string>;
45
+ valuesIterator(): SetIterator<string>;
46
+ values(): SetIterator<string>;
47
+ forEach(callbackfn: (value: string, value2: string, set: ScannerStateSetView) => void, thisArg?: unknown): void;
48
+ [Symbol.iterator](): SetIterator<string>;
49
+ }
50
+ declare class MasterCSSScanner extends EventEmitter implements AsyncDisposable {
51
+ customOptions: MasterCSSScannerOptions;
52
+ cwd: string;
53
+ readonly latentClasses: ScannerStateSetView;
54
+ readonly validClasses: ScannerStateSetView;
55
+ readonly invalidClasses: ScannerStateSetView;
56
+ readonly nativeClassNames: ScannerStateSetView;
57
+ readonly usedNativeClasses: ScannerStateSetView;
58
+ /** Current successful source snapshot, including empty and virtual inputs. */
59
+ get sources(): {
60
+ source: string;
61
+ owner: string;
62
+ parentSource: string | null;
63
+ kind: import("@master/css-binding/protocol").MasterCSSSourceExtractorKind;
64
+ extractor: string;
65
+ candidates: string[];
66
+ }[];
67
+ initialized: boolean;
68
+ initializing?: Promise<this>;
69
+ resetDependencies: string[];
70
+ readonly css: ScannerCSSView;
71
+ private bindingSession?;
72
+ private bindingState?;
73
+ private currentManifest;
74
+ private lifecycleGeneration;
75
+ private sourcePolicy?;
76
+ private sourceRevisions;
77
+ private ownerRevisions;
78
+ private ownerSnapshots;
79
+ private sourceParents;
80
+ private committedSources;
81
+ private extractionCache;
82
+ /** Precompiled minimatch patterns for per-module allow/exclude checks. */
83
+ private sourceMatchers?;
84
+ private sourceMatcherOptions?;
85
+ constructor(customOptions: MasterCSSScannerOptions, cwd?: string);
86
+ init(customOptions?: MasterCSSScannerOptions): Promise<this>;
87
+ private initInternal;
88
+ reset(customOptions?: MasterCSSScannerOptions, resetOptions?: ScannerResetOptions): Promise<this>;
89
+ dispose(): Promise<this>;
90
+ [Symbol.asyncDispose](): Promise<void>;
91
+ private insertSafelist;
92
+ /**
93
+ * @description Extract source content candidates.
94
+ * @param source
95
+ * @param content
96
+ * @returns Promise<string[]> Latent classes
97
+ */
98
+ collectCandidates(source: string, content: string, options?: ScannerSourceOptions): Promise<string[]>;
99
+ private extractCandidates;
100
+ scan(source: string, content: string): Promise<boolean>;
101
+ scanSource(source: string, content: string, options?: ScannerSourceOptions): Promise<MasterCSSScannerSourceResult>;
102
+ removeSource(source: string, options?: ScannerSourceOptions): MasterCSSScannerSourceResult;
103
+ private sourceAncestors;
104
+ reconcileSources(owner: string, inputs: readonly ScannerSourceInput[]): Promise<MasterCSSScannerSourceResult>;
105
+ private reconcileOwner;
106
+ removeOwner(owner: string): MasterCSSScannerSourceResult;
107
+ private applyUpdate;
108
+ scanModule(source: string, content: string): Promise<boolean>;
109
+ private resolveSourceAdapter;
110
+ private getSourceMatchers;
111
+ isModuleAllowed(source: string): boolean;
112
+ isSourceAllowed(source: string, options?: {
113
+ explicit?: boolean;
114
+ }): boolean;
115
+ get sourcePolicyDependencies(): string[];
116
+ registerNativeClasses(owner: string, classNames: string[]): boolean;
117
+ private getBindingSession;
118
+ private syncBindingState;
119
+ get state(): import("@master/css-binding/protocol").MasterCSSScannerState;
120
+ /**
121
+ * computed from `options.manifest`
122
+ */
123
+ get manifest(): MasterCSSManifest;
124
+ readonly slotCSSRule = "#master-css-slot{--slot:0}";
125
+ }
126
+ declare function createScanner(options: MasterCSSScannerOptions, cwd?: string): Promise<MasterCSSScanner>;
127
+ interface MasterCSSScanner {
128
+ options: MasterCSSScannerOptions;
129
+ }
130
+ export { MasterCSSScanner, MasterCSSScannerSourceResult, ScannerCSSView, type ScannerSourceInput, type ScannerSourceOptions, ScannerStateSetView, createScanner };
@@ -0,0 +1,480 @@
1
+ import { SourcePolicy } from "./source-policy.js";
2
+ import { defaultScannerOptions } from "./options/index.js";
3
+ import { matchesSourceAdapter } from "../source/adapters/types.js";
4
+ import { svelteAdapter } from "../source/adapters/svelte.js";
5
+ import { vueAdapter } from "../source/adapters/vue.js";
6
+ import { createScannerSession, serializeScannerBlocklist } from "./binding-session.js";
7
+ import { Minimatch } from "minimatch";
8
+ import { createConsola } from "consola";
9
+ import { defu } from "defu";
10
+ import { EventEmitter } from "node:events";
11
+ import path from "path";
12
+ // src/scanner/core.ts
13
+ const builtInAdapters = [vueAdapter(), svelteAdapter()];
14
+ const sourceLikeExtensions = /* @__PURE__ */ new Set([
15
+ ".html",
16
+ ".htm",
17
+ ".js",
18
+ ".mjs",
19
+ ".jsx",
20
+ ".cjs",
21
+ ".ts",
22
+ ".tsx",
23
+ ".mts",
24
+ ".cts",
25
+ ".svelte",
26
+ ".astro",
27
+ ".vue",
28
+ ".md",
29
+ ".mdx",
30
+ ".pug",
31
+ ".php"
32
+ ]);
33
+ const sourceMatchOptions = { dot: true };
34
+ const logger = createConsola({ level: 3 });
35
+ function createSourceMatchers(patterns) {
36
+ return (patterns || []).map((pattern) => new Minimatch(String(pattern), sourceMatchOptions));
37
+ }
38
+ function matchesAnySource(sources, matchers) {
39
+ for (const matcher of matchers) for (const source of sources) if (matcher.match(source)) return true;
40
+ return false;
41
+ }
42
+ function isStyleModuleRequest(source) {
43
+ const queryStart = source.indexOf("?");
44
+ if (queryStart === -1) return false;
45
+ return new URLSearchParams(source.slice(queryStart + 1)).get("type") === "style";
46
+ }
47
+ function cleanSourceRequest(source) {
48
+ const queryStart = source.indexOf("?");
49
+ return queryStart === -1 ? source : source.slice(0, queryStart);
50
+ }
51
+ function isSourceLikeModule(source) {
52
+ return sourceLikeExtensions.has(path.extname(cleanSourceRequest(source)));
53
+ }
54
+ function toPosixPath(source) {
55
+ return source.replace(/\\/g, "/");
56
+ }
57
+ function isRelativeToCwd(source) {
58
+ return source !== "" && !source.startsWith("..") && !path.isAbsolute(source);
59
+ }
60
+ function createSourceMatchCandidates(source, cwd) {
61
+ const cleanSource = cleanSourceRequest(source);
62
+ const candidates = [toPosixPath(cleanSource)];
63
+ if (path.isAbsolute(cleanSource)) {
64
+ const relativeSource = path.relative(path.resolve(cwd), cleanSource);
65
+ if (isRelativeToCwd(relativeSource)) {
66
+ const normalizedRelativeSource = toPosixPath(relativeSource);
67
+ if (!candidates.includes(normalizedRelativeSource)) candidates.push(normalizedRelativeSource);
68
+ }
69
+ }
70
+ return candidates;
71
+ }
72
+ var ScannerCSSView = class {
73
+ scanner;
74
+ constructor(scanner) {
75
+ this.scanner = scanner;
76
+ }
77
+ get manifest() {
78
+ return this.scanner.manifest;
79
+ }
80
+ get text() {
81
+ return this.scanner.state.engine.text;
82
+ }
83
+ get utilitiesLayer() {
84
+ return { rules: this.scanner.state.engine.rules.filter(({ layer }) => layer === "utilities").map((rule) => ({
85
+ ...rule,
86
+ name: rule.className
87
+ })) };
88
+ }
89
+ };
90
+ var ScannerStateSetView = class {
91
+ readValues;
92
+ [Symbol.toStringTag] = "ScannerStateSetView";
93
+ constructor(readValues) {
94
+ this.readValues = readValues;
95
+ }
96
+ get size() {
97
+ return this.readValues().length;
98
+ }
99
+ has(value) {
100
+ return this.readValues().includes(value);
101
+ }
102
+ entries() {
103
+ return new Set(this.readValues()).entries();
104
+ }
105
+ keys() {
106
+ return this.readValues()[Symbol.iterator]();
107
+ }
108
+ valuesIterator() {
109
+ return this.keys();
110
+ }
111
+ values() {
112
+ return this.keys();
113
+ }
114
+ forEach(callbackfn, thisArg) {
115
+ for (const value of this.values()) callbackfn.call(thisArg, value, value, this);
116
+ }
117
+ [Symbol.iterator]() {
118
+ return this.keys();
119
+ }
120
+ };
121
+ var MasterCSSScanner = class extends EventEmitter {
122
+ customOptions;
123
+ cwd;
124
+ latentClasses = new ScannerStateSetView(() => this.state.latentClasses);
125
+ validClasses = new ScannerStateSetView(() => this.state.validClasses);
126
+ invalidClasses = new ScannerStateSetView(() => this.state.invalidClasses);
127
+ nativeClassNames = new ScannerStateSetView(() => this.state.nativeClasses || []);
128
+ usedNativeClasses = new ScannerStateSetView(() => this.state.usedNativeClasses || []);
129
+ /** Current successful source snapshot, including empty and virtual inputs. */
130
+ get sources() {
131
+ return this.state.sources;
132
+ }
133
+ initialized = false;
134
+ initializing;
135
+ resetDependencies = [];
136
+ css = new ScannerCSSView(this);
137
+ bindingSession;
138
+ bindingState;
139
+ currentManifest;
140
+ lifecycleGeneration = 0;
141
+ sourcePolicy;
142
+ sourceRevisions = /* @__PURE__ */ new Map();
143
+ ownerRevisions = /* @__PURE__ */ new Map();
144
+ ownerSnapshots = /* @__PURE__ */ new Map();
145
+ sourceParents = /* @__PURE__ */ new Map();
146
+ committedSources = /* @__PURE__ */ new Map();
147
+ extractionCache = /* @__PURE__ */ new Map();
148
+ /** Precompiled minimatch patterns for per-module allow/exclude checks. */
149
+ sourceMatchers;
150
+ sourceMatcherOptions;
151
+ constructor(customOptions, cwd = process.cwd()) {
152
+ super();
153
+ this.customOptions = customOptions;
154
+ this.cwd = cwd;
155
+ this.currentManifest = customOptions.manifest;
156
+ }
157
+ init(customOptions = this.customOptions) {
158
+ if (this.initialized) return Promise.resolve(this);
159
+ if (this.initializing) return this.initializing;
160
+ const generation = ++this.lifecycleGeneration;
161
+ let initializing;
162
+ initializing = this.initInternal(customOptions, generation).finally(() => {
163
+ if (this.initializing === initializing) this.initializing = void 0;
164
+ });
165
+ this.initializing = initializing;
166
+ return initializing;
167
+ }
168
+ async initInternal(customOptions, generation) {
169
+ if (typeof customOptions !== "object" || customOptions === null || Array.isArray(customOptions)) throw new TypeError("MasterCSSScanner options must be an object.");
170
+ const options = defu(customOptions, defaultScannerOptions);
171
+ if (options.verbose && options.verbose > 1) {
172
+ logger.success("options");
173
+ logger.log(options);
174
+ logger.log("");
175
+ }
176
+ const bindingSession = await createScannerSession(options.manifest, {
177
+ binding: options.binding,
178
+ native: options.native,
179
+ wasm: options.wasm
180
+ });
181
+ if (generation !== this.lifecycleGeneration) {
182
+ bindingSession.dispose();
183
+ return this;
184
+ }
185
+ this.options = options;
186
+ this.customOptions = customOptions;
187
+ this.resetDependencies = [];
188
+ this.committedSources.clear();
189
+ this.extractionCache.clear();
190
+ this.sourceRevisions.clear();
191
+ this.ownerRevisions.clear();
192
+ this.ownerSnapshots.clear();
193
+ this.sourceParents.clear();
194
+ this.sourceMatchers = void 0;
195
+ this.sourceMatcherOptions = { exclude: options.exclude };
196
+ this.getSourceMatchers();
197
+ this.currentManifest = options.manifest;
198
+ this.bindingSession = bindingSession;
199
+ try {
200
+ this.insertSafelist();
201
+ this.emit("init", options, this.manifest);
202
+ if (generation === this.lifecycleGeneration) this.initialized = true;
203
+ } catch (error) {
204
+ if (this.bindingSession === bindingSession) {
205
+ this.bindingSession = void 0;
206
+ this.bindingState = void 0;
207
+ this.initialized = false;
208
+ bindingSession.dispose();
209
+ }
210
+ throw error;
211
+ }
212
+ return this;
213
+ }
214
+ async reset(customOptions = this.customOptions, resetOptions = {}) {
215
+ this.lifecycleGeneration++;
216
+ this.bindingSession?.dispose();
217
+ this.bindingSession = void 0;
218
+ this.bindingState = void 0;
219
+ this.resetDependencies = [];
220
+ this.committedSources.clear();
221
+ this.extractionCache.clear();
222
+ this.sourceRevisions.clear();
223
+ this.ownerRevisions.clear();
224
+ this.ownerSnapshots.clear();
225
+ this.sourceParents.clear();
226
+ this.sourceMatchers = void 0;
227
+ this.sourceMatcherOptions = void 0;
228
+ this.initialized = false;
229
+ this.initializing = void 0;
230
+ const initializing = this.init(customOptions);
231
+ const generation = this.lifecycleGeneration;
232
+ await initializing;
233
+ if (generation === this.lifecycleGeneration && this.initialized && resetOptions.emit !== false) this.emit("reset");
234
+ return this;
235
+ }
236
+ async dispose() {
237
+ this.lifecycleGeneration++;
238
+ const bindingSession = this.bindingSession;
239
+ this.bindingSession = void 0;
240
+ this.bindingState = void 0;
241
+ this.resetDependencies = [];
242
+ this.committedSources.clear();
243
+ this.extractionCache.clear();
244
+ this.sourceRevisions.clear();
245
+ this.ownerRevisions.clear();
246
+ this.ownerSnapshots.clear();
247
+ this.sourceParents.clear();
248
+ this.sourceMatchers = void 0;
249
+ this.sourceMatcherOptions = void 0;
250
+ this.initialized = false;
251
+ this.initializing = void 0;
252
+ bindingSession?.dispose();
253
+ try {
254
+ this.emit("dispose");
255
+ } finally {
256
+ this.removeAllListeners();
257
+ }
258
+ return this;
259
+ }
260
+ async [Symbol.asyncDispose]() {
261
+ await this.dispose();
262
+ }
263
+ insertSafelist() {
264
+ if (this.options.safelist?.length) {
265
+ this.getBindingSession().ensureClasses([...this.options.safelist]);
266
+ this.bindingState = void 0;
267
+ if (this.options.verbose) logger.success(`${this.options.safelist.length} fixed classes inserted ${this.options.safelist.join(", ")}`);
268
+ }
269
+ }
270
+ /**
271
+ * @description Extract source content candidates.
272
+ * @param source
273
+ * @param content
274
+ * @returns Promise<string[]> Latent classes
275
+ */
276
+ async collectCandidates(source, content, options = {}) {
277
+ return [...await this.extractCandidates(source, content, options)];
278
+ }
279
+ async extractCandidates(source, content, options) {
280
+ const key = JSON.stringify([
281
+ options.owner ?? "project",
282
+ source,
283
+ options.kind ?? "auto",
284
+ options.extractor ?? ""
285
+ ]);
286
+ const cached = this.extractionCache.get(key);
287
+ if (cached?.content === content) return cached.candidates;
288
+ const adapter = options.kind && options.kind !== "auto" ? void 0 : this.resolveSourceAdapter(source);
289
+ const candidates = adapter ? await adapter.extract({
290
+ source,
291
+ content
292
+ }) : this.getBindingSession().extractCandidates(source, content, options);
293
+ this.extractionCache.set(key, {
294
+ content,
295
+ candidates
296
+ });
297
+ return candidates;
298
+ }
299
+ async scan(source, content) {
300
+ return (await this.scanSource(source, content)).changed;
301
+ }
302
+ async scanSource(source, content, options = {}) {
303
+ this.getBindingSession();
304
+ const blocklist = serializeScannerBlocklist(this.options.blocklist);
305
+ const key = JSON.stringify([options.owner ?? "project", source]);
306
+ const identity = JSON.stringify([
307
+ content,
308
+ options,
309
+ blocklist
310
+ ]);
311
+ const revision = (this.sourceRevisions.get(key) ?? 0) + 1;
312
+ this.sourceRevisions.set(key, revision);
313
+ const owner = options.owner ?? "project";
314
+ const ancestors = this.sourceAncestors(owner, source, /* @__PURE__ */ new Map([[key, options.parentSource]]));
315
+ const ownerRevision = this.ownerRevisions.get(owner) ?? 0;
316
+ const snapshot = this.ownerSnapshots.get(owner);
317
+ const generation = this.lifecycleGeneration;
318
+ const candidates = await this.extractCandidates(source, content, options);
319
+ await snapshot?.catch(() => void 0);
320
+ if (ancestors.some(([parent, revision]) => (this.sourceRevisions.get(parent) ?? 0) !== revision)) return {
321
+ changed: false,
322
+ sourceChanged: false,
323
+ candidates
324
+ };
325
+ if (generation !== this.lifecycleGeneration || revision !== this.sourceRevisions.get(key) || ownerRevision !== (this.ownerRevisions.get(owner) ?? 0)) return {
326
+ changed: false,
327
+ sourceChanged: false,
328
+ candidates
329
+ };
330
+ if (this.committedSources.get(key) === identity) return {
331
+ changed: false,
332
+ sourceChanged: false,
333
+ candidates
334
+ };
335
+ const update = this.getBindingSession().scanCandidates(source, content, candidates, blocklist, options);
336
+ if (options.parentSource) this.sourceParents.set(key, options.parentSource);
337
+ else this.sourceParents.delete(key);
338
+ const result = this.applyUpdate(update);
339
+ this.committedSources.set(key, identity);
340
+ return result;
341
+ }
342
+ removeSource(source, options = {}) {
343
+ const key = JSON.stringify([options.owner ?? "project", source]);
344
+ this.sourceRevisions.set(key, (this.sourceRevisions.get(key) ?? 0) + 1);
345
+ this.committedSources.clear();
346
+ return this.applyUpdate(this.getBindingSession().removeSource(source, options));
347
+ }
348
+ sourceAncestors(owner, source, proposed = /* @__PURE__ */ new Map()) {
349
+ const ancestors = [];
350
+ const seen = /* @__PURE__ */ new Set();
351
+ let key = JSON.stringify([owner, source]);
352
+ while (!seen.has(key)) {
353
+ seen.add(key);
354
+ ancestors.push([key, this.sourceRevisions.get(key) ?? 0]);
355
+ const parent = proposed.has(key) ? proposed.get(key) : this.sourceParents.get(key);
356
+ if (!parent) break;
357
+ key = JSON.stringify([owner, parent]);
358
+ }
359
+ return ancestors;
360
+ }
361
+ reconcileSources(owner, inputs) {
362
+ const task = this.reconcileOwner(owner, inputs);
363
+ this.ownerSnapshots.set(owner, task);
364
+ task.finally(() => {
365
+ if (this.ownerSnapshots.get(owner) === task) this.ownerSnapshots.delete(owner);
366
+ }).catch(() => void 0);
367
+ return task;
368
+ }
369
+ async reconcileOwner(owner, inputs) {
370
+ const revision = (this.ownerRevisions.get(owner) ?? 0) + 1;
371
+ this.ownerRevisions.set(owner, revision);
372
+ const generation = this.lifecycleGeneration;
373
+ const proposed = new Map(inputs.map((input) => [JSON.stringify([owner, input.source]), input.options?.parentSource]));
374
+ const revisions = new Map(inputs.map((input) => [input.source, this.sourceAncestors(owner, input.source, proposed)]));
375
+ const prepared = await Promise.all(inputs.map(async (input) => {
376
+ const options = {
377
+ ...input.options,
378
+ owner
379
+ };
380
+ return {
381
+ ...input,
382
+ options,
383
+ candidates: input.candidates ?? await this.extractCandidates(input.source, input.content, options),
384
+ blocklist: input.blocklist ?? serializeScannerBlocklist(this.options.blocklist)
385
+ };
386
+ }));
387
+ if (generation !== this.lifecycleGeneration || revision !== this.ownerRevisions.get(owner)) return {
388
+ changed: false,
389
+ sourceChanged: false,
390
+ candidates: prepared.flatMap((input) => input.candidates)
391
+ };
392
+ const retained = prepared.filter((input) => revisions.get(input.source).every(([key, revision]) => (this.sourceRevisions.get(key) ?? 0) === revision));
393
+ const update = this.getBindingSession().reconcileSources(owner, retained);
394
+ this.committedSources.clear();
395
+ for (const key of this.sourceParents.keys()) if (JSON.parse(key)[0] === owner) this.sourceParents.delete(key);
396
+ for (const input of retained) if (input.options.parentSource) this.sourceParents.set(JSON.stringify([owner, input.source]), input.options.parentSource);
397
+ return this.applyUpdate(update);
398
+ }
399
+ removeOwner(owner) {
400
+ this.ownerRevisions.set(owner, (this.ownerRevisions.get(owner) ?? 0) + 1);
401
+ this.committedSources.clear();
402
+ return this.applyUpdate(this.getBindingSession().removeOwner(owner));
403
+ }
404
+ applyUpdate(update) {
405
+ this.bindingState = void 0;
406
+ const inserted = [...update.validClasses, ...update.usedNativeClasses ?? []];
407
+ if (inserted.length && this.options.verbose) logger.success(`${inserted.length} classes inserted${this.options.verbose > 1 ? " " + inserted.join(", ") : ""}`);
408
+ if (update.changed) this.emit("change");
409
+ return {
410
+ changed: update.changed,
411
+ sourceChanged: update.sourceChanged,
412
+ candidates: update.candidates
413
+ };
414
+ }
415
+ async scanModule(source, content) {
416
+ if (!this.isModuleAllowed(source)) return false;
417
+ return this.scan(source, content);
418
+ }
419
+ resolveSourceAdapter(source) {
420
+ return builtInAdapters.find((adapter) => matchesSourceAdapter(adapter, source));
421
+ }
422
+ getSourceMatchers() {
423
+ const { exclude } = this.options;
424
+ if (!this.sourceMatchers || this.sourceMatcherOptions?.exclude !== exclude) {
425
+ const explicitExclude = this.sourceMatcherOptions?.exclude !== exclude ? exclude : this.customOptions.exclude;
426
+ this.sourcePolicy = new SourcePolicy(this.cwd, {
427
+ exclude: explicitExclude,
428
+ outputDirectories: this.options.outputDirectories
429
+ });
430
+ this.sourceMatcherOptions = { exclude };
431
+ this.sourceMatchers = { exclude: createSourceMatchers(exclude) };
432
+ }
433
+ return this.sourceMatchers;
434
+ }
435
+ isModuleAllowed(source) {
436
+ if (!source || source.startsWith("\0")) return false;
437
+ if (isStyleModuleRequest(source)) return false;
438
+ if (!isSourceLikeModule(source)) return false;
439
+ const sources = createSourceMatchCandidates(source, this.cwd);
440
+ const { exclude } = this.getSourceMatchers();
441
+ if (exclude.length && matchesAnySource(sources, exclude)) return false;
442
+ return this.sourcePolicy?.accepts(cleanSourceRequest(source)) ?? false;
443
+ }
444
+ isSourceAllowed(source, options = {}) {
445
+ this.getSourceMatchers();
446
+ return this.sourcePolicy?.accepts(source, options.explicit) ?? false;
447
+ }
448
+ get sourcePolicyDependencies() {
449
+ return [...this.sourcePolicy?.dependencies ?? []];
450
+ }
451
+ registerNativeClasses(owner, classNames) {
452
+ const changed = this.getBindingSession().registerNativeClasses(owner, classNames);
453
+ this.bindingState = void 0;
454
+ if (changed) this.emit("change");
455
+ return changed;
456
+ }
457
+ getBindingSession() {
458
+ if (!this.bindingSession) throw new Error("MasterCSSScanner must be initialized before use.");
459
+ return this.bindingSession;
460
+ }
461
+ syncBindingState() {
462
+ const state = this.getBindingSession().state();
463
+ this.bindingState = state;
464
+ return state;
465
+ }
466
+ get state() {
467
+ return this.bindingState || this.syncBindingState();
468
+ }
469
+ /**
470
+ * computed from `options.manifest`
471
+ */
472
+ get manifest() {
473
+ return this.currentManifest;
474
+ }
475
+ slotCSSRule = "#master-css-slot{--slot:0}";
476
+ };
477
+ async function createScanner(options, cwd = process.cwd()) {
478
+ return await new MasterCSSScanner(options, cwd).init();
479
+ }
480
+ export { MasterCSSScanner, ScannerCSSView, ScannerStateSetView, createScanner };
@@ -0,0 +1,3 @@
1
+ import { MasterCSSScannerConfiguration, MasterCSSScannerOptions, defaultScannerOptions } from "./options/index.js";
2
+ import { MasterCSSScanner, MasterCSSScannerSourceResult, createScanner } from "./core.js";
3
+ export { MasterCSSScanner, type MasterCSSScannerConfiguration, type MasterCSSScannerOptions, type MasterCSSScannerSourceResult, createScanner, defaultScannerOptions };
@@ -0,0 +1,3 @@
1
+ import { defaultScannerOptions } from "./options/index.js";
2
+ import { MasterCSSScanner, createScanner } from "./core.js";
3
+ export { MasterCSSScanner, createScanner, defaultScannerOptions };
@@ -0,0 +1,5 @@
1
+ import { ScannerSourceInput, ScannerSourceOptions } from "./binding-session.js";
2
+ import { MasterCSSScannerConfiguration, MasterCSSScannerOptions, defaultScannerOptions } from "./options/index.js";
3
+ import { MasterCSSScanner, MasterCSSScannerSourceResult, createScanner } from "./core.js";
4
+ import { SourcePolicy, SourcePolicyOptions } from "./source-policy.js";
5
+ export { MasterCSSScanner, type MasterCSSScannerConfiguration, type MasterCSSScannerOptions, type MasterCSSScannerSourceResult, type ScannerSourceInput, type ScannerSourceOptions, SourcePolicy, type SourcePolicyOptions, createScanner, defaultScannerOptions };
@@ -0,0 +1,4 @@
1
+ import { SourcePolicy } from "./source-policy.js";
2
+ import { defaultScannerOptions } from "./options/index.js";
3
+ import { MasterCSSScanner, createScanner } from "./core.js";
4
+ export { MasterCSSScanner, SourcePolicy, createScanner, defaultScannerOptions };
@@ -0,0 +1,16 @@
1
+ import { MasterCSSBindingLoadOptions } from "@master/css-binding/tooling";
2
+ import { MasterCSSManifest } from "@master/css-schema/manifest";
3
+ interface MasterCSSScannerConfiguration {
4
+ verbose?: number;
5
+ path?: string;
6
+ /** Actual bundler output directories, always excluded from source input. */
7
+ outputDirectories?: readonly string[];
8
+ exclude?: readonly string[];
9
+ safelist?: readonly string[];
10
+ blocklist?: readonly (string | RegExp)[];
11
+ }
12
+ interface MasterCSSScannerOptions extends MasterCSSScannerConfiguration, MasterCSSBindingLoadOptions {
13
+ manifest: MasterCSSManifest;
14
+ }
15
+ declare const defaultScannerOptions: Readonly<MasterCSSScannerConfiguration>;
16
+ export { MasterCSSScannerConfiguration, MasterCSSScannerOptions, defaultScannerOptions };