mikuru 1.0.24 → 1.0.26
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/CHANGELOG.md +11 -1
- package/README.md +9 -6
- package/dist/compiler/compile.js +14 -0
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/css.d.ts +22 -0
- package/dist/compiler/css.js +529 -0
- package/dist/compiler/css.js.map +1 -0
- package/dist/compiler/generate.js +9 -26
- package/dist/compiler/generate.js.map +1 -1
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/runtime/devtools.d.ts +5 -0
- package/dist/runtime/devtools.js +15 -0
- package/dist/runtime/devtools.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.0.
|
|
3
|
+
## 1.0.26 - 2026-05-15
|
|
4
|
+
|
|
5
|
+
- Improved scoped CSS rewriting with native CSS nesting, `@starting-style`, additional raw descriptor at-rules, and stronger `:deep(...)` / `:global(...)` parsing around attributes and functional pseudo-class arguments.
|
|
6
|
+
- Added scoped CSS malformed-block diagnostics with offset, line, column, and one-line code frames, including nested CSS block locations and debug `compiler:warning` style payloads.
|
|
7
|
+
- Expanded the dogfood Debug Panel with compiler/style diagnostic message, phase, location, code frame details, event search coverage, and snapshot/copy coverage for diagnostic fields.
|
|
8
|
+
- Added fixture-style scoped CSS compiler coverage and package usage smoke checks for public `compileStyle()` exports, scoped rewriting, and malformed diagnostic fields.
|
|
9
|
+
|
|
10
|
+
## 1.0.25 - 2026-05-15
|
|
4
11
|
|
|
5
12
|
- Added Mikuru-native `m-*` directive syntax across DOM rendering, SSR, and hydration while keeping `v-*` as compatibility aliases.
|
|
6
13
|
- Added debug compiler warnings for `v-*` compatibility aliases, pointing users to the matching `m-*` directive spelling.
|
|
7
14
|
- Updated README, docs, examples, and templates to present `m-*` as the recommended directive syntax.
|
|
8
15
|
- Added shared Debug Inspector diagnostic payloads for compiler, runtime, router, SSR, and hydration warning/error events.
|
|
16
|
+
- Expanded the dogfood Debug Panel with a searchable and collapsible component tree, component root reveal, event filters/search, event-to-component navigation, and copyable compact debug snapshots.
|
|
17
|
+
- Split dogfood Debug Panel filtering, tree, formatting, and snapshot logic into tested example helpers while documenting that they are not public package exports.
|
|
18
|
+
- Improved scoped CSS rewriting coverage for complex selectors, nested at-rules, comments, strings, and malformed CSS diagnostics.
|
|
9
19
|
- Added support for `<template m-if>` / `<template m-else-if>` / `<template m-else>` fragment branches across DOM rendering, SSR, and hydration.
|
|
10
20
|
- Added support for keyed `<template m-for>` fragment rows across DOM rendering, SSR, and hydration.
|
|
11
21
|
- Expanded `<template m-for>` coverage for unkeyed fragments, nested fragment loops, and nested `m-if` / `m-else` branches.
|
package/README.md
CHANGED
|
@@ -135,21 +135,22 @@ declare const Greeting: MikuruComponent<GreetingProps>;
|
|
|
135
135
|
- DOM events with `@click`, `m-on:click`, inline handlers, object-form option modifiers, `.prevent`, `.stop`, `.self`, `.once`, `.capture`, `.passive`, key, mouse button, system, and `.exact` modifiers
|
|
136
136
|
- Component events with `@select` and `.once`
|
|
137
137
|
- Attribute bindings with normalized `:class` and `:style`, boolean/form property sync, direct/object `m-bind` modifiers like `.prop`, `.attr`, and `.camel`, plus dynamic arguments like `:[name]` and `@[event]`
|
|
138
|
-
- `m-if`, `m-else-if`, `m-else`, `m-show`, `m-for`, `m-html`, `m-text`, `m-pre`, and `m-
|
|
138
|
+
- `m-if`, `m-else-if`, `m-else`, `m-show`, `m-for`, `m-html`, `m-text`, `m-pre`, `m-cloak`, `m-once`, and `m-memo`
|
|
139
139
|
- `m-model` for common form controls, checkbox arrays, radio groups, multiple selects, modifiers, and named child component models
|
|
140
140
|
- `v-*` directive spellings remain available as compatibility aliases for existing components and Vue-oriented migrations
|
|
141
|
-
- Component props, events, DOM attribute fallthrough, `useAttrs`, template refs, `defineProps`, `defineEmits`, default slots, named/dynamic slots, and slot props with simple defaults
|
|
141
|
+
- Component props, events, DOM attribute fallthrough, `useAttrs`, template refs, dynamic `<component :is>`, `defineProps`, `defineEmits`, default slots, named/dynamic slots, and slot props with simple defaults
|
|
142
142
|
- CSS class transitions with built-in `<Transition name="fade">`, `m-if` chains, dynamic components, class overrides, `appear`, `mode="out-in"`, and `<TransitionGroup>` for keyed lists
|
|
143
143
|
- Built-in `<Teleport to="#target">` for rendering content outside the current DOM position
|
|
144
144
|
- Built-in `<AsyncBoundary :loading :fallback :delay :timeout>` for grouped async loading, delayed loading UI, boundary timeouts, and retryable async failures with aggregated fallback errors
|
|
145
145
|
- Built-in `<ErrorBoundary :fallback>` for local component mount, descendant event handler, lifecycle, and cleanup fallbacks, with `errorInfo`, `retry`, `reset`, and `:reset-key` recovery
|
|
146
|
+
- Built-in `<KeepAlive>` for caching one dynamic child component with `include`, `exclude`, and `max` controls
|
|
146
147
|
- Runtime helpers including `ref`, `isRef`, `unref`, `toRef`, `toRefs`, `reactive`, `readonly`, lazy cached read-only and writable `computed`, `effect` with optional scheduling, `queueJob`/`flushJobs`, `watch`, `watchEffect` with cleanup callbacks, `nextTick`, lifecycle callbacks including KeepAlive activation hooks, `provide`, `inject`, and `defineAsyncComponent` with ErrorBoundary handoff and SSR loader resolution
|
|
147
148
|
- Routing through `mikuru/router` with route matching, history/hash/memory histories, guards, router context helpers, and `RouterView` / `RouterLink` across mount, SSR, and hydration
|
|
148
149
|
- SSR through `compileSsr()` and `mikuru/server`, covering escaped text, static and bound attributes, content directives, `m-pre`, `m-cloak`, `m-if` chains, `m-for`, async child components, props, named/default slots, scoped slot props, component tree context, Teleport collection, string and async iterable stream rendering, and router route rendering with context propagation
|
|
149
150
|
- Hydration through `compileHydration()` and `hydrateRoute()`, reusing existing SSR DOM while attaching events, syncing text/attributes, recovering structural mismatches with an opt-out remount fallback, hydrating component context/lifecycle hooks, `m-show`, DOM and component `m-model`, `m-pre`, `m-cloak`, initial `m-if` / `m-for` DOM, Teleport target and disabled inline content, delegating child and route components to `hydrate()` when available, and optionally starting router history listening after route hydration
|
|
150
|
-
- Style injection and
|
|
151
|
+
- Style injection and `<style scoped>` selector rewriting for common selectors, native CSS nesting, `:global(...)`, `:deep(...)`, nested at-rules, and malformed CSS diagnostics
|
|
151
152
|
- Compile errors with filenames, line/column information, code frames, and typo suggestions for built-in attributes, directives, and modifiers
|
|
152
|
-
- Debug diagnostics with optional generated `sourceURL`, `v-*` compatibility warnings, unstable devtools metadata/events, and hydration warnings that include phase, component, and filename context
|
|
153
|
+
- Debug diagnostics with optional generated `sourceURL`, `v-*` compatibility warnings, unstable devtools metadata/events, compiler/style diagnostic locations and frames, and hydration warnings that include phase, component, and filename context
|
|
153
154
|
|
|
154
155
|
## Package Exports
|
|
155
156
|
|
|
@@ -180,12 +181,14 @@ import "mikuru/env";
|
|
|
180
181
|
Compiler, runtime, and server entries are public for lower-level integrations:
|
|
181
182
|
|
|
182
183
|
```ts
|
|
183
|
-
import { compile, compileHydration, compileSsr } from "mikuru/compiler";
|
|
184
|
+
import { compile, compileHydration, compileSsr, compileStyle } from "mikuru/compiler";
|
|
184
185
|
import { effect, isRef, nextTick, reactive, readonly, ref, toRef, toRefs, unref, unwrap, watch } from "mikuru/runtime";
|
|
185
186
|
import { hydrateRoute, renderComponentToString, renderRouteToString, renderToStream, renderToString } from "mikuru/server";
|
|
186
187
|
import type { MikuruAsyncBoundaryFallbackProps, MikuruErrorBoundaryFallbackProps, MikuruErrorInfo, MikuruErrorPhase } from "mikuru/runtime";
|
|
187
188
|
```
|
|
188
189
|
|
|
190
|
+
`compileStyle()` returns scoped CSS code plus non-throwing diagnostics. Malformed scoped CSS diagnostics include `offset`, `line`, `column`, and a one-line `frame`, and debug builds emit the same fields in `compiler:warning` style diagnostic events.
|
|
191
|
+
|
|
189
192
|
The package also provides the `mikuru` binary:
|
|
190
193
|
|
|
191
194
|
```sh
|
|
@@ -227,7 +230,7 @@ const open = ref(false);
|
|
|
227
230
|
|
|
228
231
|
Mikuru does not claim Vue compatibility. The v1 package does not include stable devtools or full template type checking.
|
|
229
232
|
|
|
230
|
-
Scoped CSS is
|
|
233
|
+
Scoped CSS is supported for common selector rewriting, native CSS nesting, `:global(...)`, `:deep(...)`, nested at-rules such as `@media`, `@scope`, and `@starting-style`, and malformed block diagnostics. What is not included in v1 is a full CSS compiler: CSS Modules, preprocessors, and project-level CSS transforms stay outside the package.
|
|
231
234
|
|
|
232
235
|
## Repository Development
|
|
233
236
|
|
package/dist/compiler/compile.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { analyzeTemplate } from "./analyzeTemplate.js";
|
|
2
2
|
import { emitCompatDirectiveDiagnostics } from "./compatDiagnostics.js";
|
|
3
|
+
import { compileDescriptorStyle } from "./css.js";
|
|
3
4
|
import { generate } from "./generate.js";
|
|
4
5
|
import { parseSfc } from "./parseSfc.js";
|
|
5
6
|
import { parseTemplate } from "./parseTemplate.js";
|
|
@@ -14,6 +15,19 @@ export function compile(source, options = {}) {
|
|
|
14
15
|
offset: descriptor.templateOffset
|
|
15
16
|
});
|
|
16
17
|
emitCompatDirectiveDiagnostics(ast, { debug: options.debug === true, filename: options.filename, phase: "compile" });
|
|
18
|
+
if (options.debug === true && descriptor.style?.trim()) {
|
|
19
|
+
const styleResult = compileDescriptorStyle(descriptor, descriptor.styleScoped ? `data-mikuru-scope-preview` : undefined);
|
|
20
|
+
for (const diagnostic of styleResult.diagnostics) {
|
|
21
|
+
emitDebugDiagnostic("compiler", diagnostic.level, diagnostic.message, {
|
|
22
|
+
phase: "style",
|
|
23
|
+
filename: options.filename,
|
|
24
|
+
offset: diagnostic.offset,
|
|
25
|
+
line: diagnostic.line,
|
|
26
|
+
column: diagnostic.column,
|
|
27
|
+
frame: diagnostic.frame
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
17
31
|
const bindings = analyzeTemplate(ast, { source, filename: options.filename });
|
|
18
32
|
const code = generate(descriptor, ast, { debug: options.debug === true, batchedUpdates: options.batchedUpdates === true });
|
|
19
33
|
const map = createSourceMap(code, descriptor, ast);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,UAA0B,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,MAAM,EAAE,UAAU,CAAC,cAAc;SAClC,CAAC,CAAC;QACH,8BAA8B,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACrH,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3H,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAEnD,OAAO;YACL,IAAI;YACJ,GAAG;YACH,UAAU;YACV,GAAG;YACH,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/F,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/compiler/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,UAA0B,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM;YACN,MAAM,EAAE,UAAU,CAAC,cAAc;SAClC,CAAC,CAAC;QACH,8BAA8B,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACrH,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;YACvD,MAAM,WAAW,GAAG,sBAAsB,CAAC,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACzH,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;gBACjD,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,EAAE;oBACpE,KAAK,EAAE,OAAO;oBACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,KAAK,EAAE,UAAU,CAAC,KAAK;iBACxB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3H,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;QAEnD,OAAO;YACL,IAAI;YACJ,GAAG;YACH,UAAU;YACV,GAAG;YACH,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mBAAmB,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/F,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK;SACN,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SfcDescriptor } from "./types.js";
|
|
2
|
+
export type CssCompileDiagnostic = {
|
|
3
|
+
level: "warning";
|
|
4
|
+
message: string;
|
|
5
|
+
offset?: number;
|
|
6
|
+
line?: number;
|
|
7
|
+
column?: number;
|
|
8
|
+
frame?: string;
|
|
9
|
+
};
|
|
10
|
+
export type CssCompileOptions = {
|
|
11
|
+
filename?: string;
|
|
12
|
+
scoped?: boolean;
|
|
13
|
+
scopeAttr?: string;
|
|
14
|
+
};
|
|
15
|
+
export type CssCompileResult = {
|
|
16
|
+
code: string;
|
|
17
|
+
scoped: boolean;
|
|
18
|
+
scopeAttr?: string;
|
|
19
|
+
diagnostics: CssCompileDiagnostic[];
|
|
20
|
+
};
|
|
21
|
+
export declare function compileStyle(css: string, options?: CssCompileOptions): CssCompileResult;
|
|
22
|
+
export declare function compileDescriptorStyle(descriptor: SfcDescriptor, scopeAttr?: string): CssCompileResult;
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { createCodeFrame, getSourceLocation } from "./errors.js";
|
|
2
|
+
const nestedRuleAtRules = new Set(["media", "supports", "container", "layer", "scope", "starting-style"]);
|
|
3
|
+
const rawBlockAtRules = new Set([
|
|
4
|
+
"keyframes",
|
|
5
|
+
"-webkit-keyframes",
|
|
6
|
+
"-moz-keyframes",
|
|
7
|
+
"-o-keyframes",
|
|
8
|
+
"counter-style",
|
|
9
|
+
"font-face",
|
|
10
|
+
"font-feature-values",
|
|
11
|
+
"page",
|
|
12
|
+
"property",
|
|
13
|
+
"viewport"
|
|
14
|
+
]);
|
|
15
|
+
export function compileStyle(css, options = {}) {
|
|
16
|
+
const diagnostics = [];
|
|
17
|
+
const trimmed = css.trim();
|
|
18
|
+
const scoped = options.scoped === true && !!options.scopeAttr;
|
|
19
|
+
if (!scoped) {
|
|
20
|
+
return {
|
|
21
|
+
code: trimmed,
|
|
22
|
+
scoped: false,
|
|
23
|
+
diagnostics
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
code: scopeCss(trimmed, options.scopeAttr, diagnostics, trimmed),
|
|
28
|
+
scoped: true,
|
|
29
|
+
scopeAttr: options.scopeAttr,
|
|
30
|
+
diagnostics
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function compileDescriptorStyle(descriptor, scopeAttr) {
|
|
34
|
+
return compileStyle(descriptor.style ?? "", {
|
|
35
|
+
filename: descriptor.filename,
|
|
36
|
+
scoped: descriptor.styleScoped === true,
|
|
37
|
+
scopeAttr
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function scopeCss(css, scopeAttr, diagnostics, rootCss, offset = 0) {
|
|
41
|
+
let result = "";
|
|
42
|
+
let index = 0;
|
|
43
|
+
while (index < css.length) {
|
|
44
|
+
const openIndex = findNextTopLevelChar(css, "{", index);
|
|
45
|
+
if (openIndex === -1) {
|
|
46
|
+
result += css.slice(index);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
const closeIndex = findMatchingBrace(css, openIndex);
|
|
50
|
+
if (closeIndex === -1) {
|
|
51
|
+
const unclosedOpenIndex = findUnclosedOpenIndex(css, openIndex, "{", "}");
|
|
52
|
+
const diagnosticOffset = offset + unclosedOpenIndex;
|
|
53
|
+
const location = getSourceLocation(rootCss, diagnosticOffset);
|
|
54
|
+
diagnostics.push({
|
|
55
|
+
level: "warning",
|
|
56
|
+
message: "Could not scope a CSS rule because its block is missing a closing brace.",
|
|
57
|
+
offset: diagnosticOffset,
|
|
58
|
+
line: location.line,
|
|
59
|
+
column: location.column,
|
|
60
|
+
frame: createCodeFrame(rootCss, location)
|
|
61
|
+
});
|
|
62
|
+
result += css.slice(index);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
const preludeStart = findPreludeStart(css, index, openIndex);
|
|
66
|
+
const beforePrelude = css.slice(index, preludeStart);
|
|
67
|
+
const prelude = css.slice(preludeStart, openIndex);
|
|
68
|
+
const body = css.slice(openIndex + 1, closeIndex);
|
|
69
|
+
const scopedRule = scopeRule(prelude, body, scopeAttr, diagnostics, rootCss, offset + openIndex + 1);
|
|
70
|
+
result += beforePrelude + scopedRule;
|
|
71
|
+
index = closeIndex + 1;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function scopeRule(prelude, body, scopeAttr, diagnostics, rootCss, bodyOffset) {
|
|
76
|
+
const trimmedPrelude = prelude.trim();
|
|
77
|
+
if (!trimmedPrelude) {
|
|
78
|
+
return `${prelude}{${body}}`;
|
|
79
|
+
}
|
|
80
|
+
if (trimmedPrelude.startsWith("@")) {
|
|
81
|
+
const atRuleName = readAtRuleName(trimmedPrelude);
|
|
82
|
+
if (nestedRuleAtRules.has(atRuleName)) {
|
|
83
|
+
return `${prelude}{${scopeCss(body, scopeAttr, diagnostics, rootCss, bodyOffset)}}`;
|
|
84
|
+
}
|
|
85
|
+
if (rawBlockAtRules.has(atRuleName)) {
|
|
86
|
+
return `${prelude}{${body}}`;
|
|
87
|
+
}
|
|
88
|
+
return bodyContainsRules(body) ? `${prelude}{${scopeCss(body, scopeAttr, diagnostics, rootCss, bodyOffset)}}` : `${prelude}{${body}}`;
|
|
89
|
+
}
|
|
90
|
+
const scopedBody = bodyContainsRules(body) ? scopeCss(body, scopeAttr, diagnostics, rootCss, bodyOffset) : body;
|
|
91
|
+
return `${scopeSelectorList(prelude, scopeAttr)}{${scopedBody}}`;
|
|
92
|
+
}
|
|
93
|
+
function scopeSelectorList(selectorSource, scopeAttr) {
|
|
94
|
+
return splitSelectorList(selectorSource)
|
|
95
|
+
.map((selector) => scopeSingleSelector(selector, scopeAttr))
|
|
96
|
+
.join(",");
|
|
97
|
+
}
|
|
98
|
+
function scopeSingleSelector(selector, scopeAttr) {
|
|
99
|
+
const leading = selector.match(/^\s*/)?.[0] ?? "";
|
|
100
|
+
const trailing = selector.match(/\s*$/)?.[0] ?? "";
|
|
101
|
+
let body = selector.trim();
|
|
102
|
+
if (!body || body.includes(`[${scopeAttr}]`)) {
|
|
103
|
+
return selector;
|
|
104
|
+
}
|
|
105
|
+
if (isPureGlobalSelector(selector, "global")) {
|
|
106
|
+
body = unwrapFunctionalPseudo(body, "global");
|
|
107
|
+
return `${leading}${body}${trailing}`;
|
|
108
|
+
}
|
|
109
|
+
const globalMatch = findFunctionalPseudo(body, "global");
|
|
110
|
+
if (globalMatch) {
|
|
111
|
+
const prefix = body.slice(0, globalMatch.start).trimEnd();
|
|
112
|
+
const argument = globalMatch.argument.trim();
|
|
113
|
+
const suffix = body.slice(globalMatch.end).trimStart();
|
|
114
|
+
const scopedPrefix = prefix ? insertScopeAttribute(prefix, scopeAttr) : "";
|
|
115
|
+
return `${leading}${scopedPrefix}${scopedPrefix ? " " : ""}${argument}${suffix ? ` ${suffix}` : ""}${trailing}`;
|
|
116
|
+
}
|
|
117
|
+
const deepMatch = findFunctionalPseudo(body, "deep");
|
|
118
|
+
if (deepMatch) {
|
|
119
|
+
const prefix = body.slice(0, deepMatch.start).trimEnd();
|
|
120
|
+
const argument = deepMatch.argument.trim();
|
|
121
|
+
const suffix = body.slice(deepMatch.end).trimStart();
|
|
122
|
+
const scopedPrefix = prefix ? insertScopeAttribute(prefix, scopeAttr) : `[${scopeAttr}]`;
|
|
123
|
+
return `${leading}${scopedPrefix} ${argument}${suffix ? ` ${suffix}` : ""}${trailing}`;
|
|
124
|
+
}
|
|
125
|
+
return `${leading}${insertScopeAttribute(body, scopeAttr)}${trailing}`;
|
|
126
|
+
}
|
|
127
|
+
function insertScopeAttribute(selector, scopeAttr) {
|
|
128
|
+
const insertIndex = findScopeInsertIndex(selector);
|
|
129
|
+
if (insertIndex === -1) {
|
|
130
|
+
return `${selector}[${scopeAttr}]`;
|
|
131
|
+
}
|
|
132
|
+
return `${selector.slice(0, insertIndex)}[${scopeAttr}]${selector.slice(insertIndex)}`;
|
|
133
|
+
}
|
|
134
|
+
function findScopeInsertIndex(selector) {
|
|
135
|
+
let depth = 0;
|
|
136
|
+
let quote;
|
|
137
|
+
let lastCombinator = -1;
|
|
138
|
+
let firstPseudoInTarget = -1;
|
|
139
|
+
for (let index = 0; index < selector.length; index += 1) {
|
|
140
|
+
const char = selector[index];
|
|
141
|
+
const previous = selector[index - 1];
|
|
142
|
+
if (char === "\\") {
|
|
143
|
+
index += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (quote) {
|
|
147
|
+
if (char === quote && previous !== "\\") {
|
|
148
|
+
quote = undefined;
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (char === "\"" || char === "'") {
|
|
153
|
+
quote = char;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (char === "(" || char === "[") {
|
|
157
|
+
depth += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (char === ")" || char === "]") {
|
|
161
|
+
depth = Math.max(0, depth - 1);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (depth > 0) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (isCombinator(char)) {
|
|
168
|
+
lastCombinator = index;
|
|
169
|
+
firstPseudoInTarget = -1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (char === ":" && index > lastCombinator && firstPseudoInTarget === -1) {
|
|
173
|
+
firstPseudoInTarget = index;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return firstPseudoInTarget === -1 ? selector.length : firstPseudoInTarget;
|
|
177
|
+
}
|
|
178
|
+
function splitSelectorList(selectorSource) {
|
|
179
|
+
const selectors = [];
|
|
180
|
+
let start = 0;
|
|
181
|
+
let depth = 0;
|
|
182
|
+
let quote;
|
|
183
|
+
for (let index = 0; index < selectorSource.length; index += 1) {
|
|
184
|
+
const char = selectorSource[index];
|
|
185
|
+
const previous = selectorSource[index - 1];
|
|
186
|
+
if (char === "\\") {
|
|
187
|
+
index += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (quote) {
|
|
191
|
+
if (char === quote && previous !== "\\") {
|
|
192
|
+
quote = undefined;
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (char === "\"" || char === "'") {
|
|
197
|
+
quote = char;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (char === "(" || char === "[") {
|
|
201
|
+
depth += 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (char === ")" || char === "]") {
|
|
205
|
+
depth = Math.max(0, depth - 1);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (char === "," && depth === 0) {
|
|
209
|
+
selectors.push(selectorSource.slice(start, index));
|
|
210
|
+
start = index + 1;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
selectors.push(selectorSource.slice(start));
|
|
214
|
+
return selectors;
|
|
215
|
+
}
|
|
216
|
+
function unwrapFunctionalPseudo(selector, name) {
|
|
217
|
+
const match = findFunctionalPseudo(selector, name);
|
|
218
|
+
if (!match) {
|
|
219
|
+
return selector;
|
|
220
|
+
}
|
|
221
|
+
return `${selector.slice(0, match.start)}${match.argument}${selector.slice(match.end)}`;
|
|
222
|
+
}
|
|
223
|
+
function isPureGlobalSelector(selector, name) {
|
|
224
|
+
const match = findFunctionalPseudo(selector.trim(), name);
|
|
225
|
+
return match?.start === 0 && match.end === selector.trim().length;
|
|
226
|
+
}
|
|
227
|
+
function findFunctionalPseudo(selector, name) {
|
|
228
|
+
let parenDepth = 0;
|
|
229
|
+
let bracketDepth = 0;
|
|
230
|
+
let quote;
|
|
231
|
+
for (let index = 0; index < selector.length; index += 1) {
|
|
232
|
+
const char = selector[index];
|
|
233
|
+
const previous = selector[index - 1];
|
|
234
|
+
if (char === "\\") {
|
|
235
|
+
index += 1;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (quote) {
|
|
239
|
+
if (char === quote && previous !== "\\") {
|
|
240
|
+
quote = undefined;
|
|
241
|
+
}
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (char === "\"" || char === "'") {
|
|
245
|
+
quote = char;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (char === "[") {
|
|
249
|
+
bracketDepth += 1;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (char === "]") {
|
|
253
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (bracketDepth > 0) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (char === "(") {
|
|
260
|
+
parenDepth += 1;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (char === ")") {
|
|
264
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (char !== ":" || parenDepth > 0 || !matchesFunctionalPseudoName(selector, index, name)) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const argumentStart = index + name.length + 2;
|
|
271
|
+
const close = findMatchingParen(selector, argumentStart - 1);
|
|
272
|
+
if (close === -1) {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
start: index,
|
|
277
|
+
end: close + 1,
|
|
278
|
+
argument: selector.slice(argumentStart, close)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
function matchesFunctionalPseudoName(selector, colonIndex, name) {
|
|
284
|
+
if (selector[colonIndex + 1] === ":") {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
const nameStart = colonIndex + 1;
|
|
288
|
+
const nameEnd = nameStart + name.length;
|
|
289
|
+
return selector.slice(nameStart, nameEnd) === name && selector[nameEnd] === "(";
|
|
290
|
+
}
|
|
291
|
+
function findPreludeStart(css, searchStart, openIndex) {
|
|
292
|
+
let boundary = searchStart;
|
|
293
|
+
let quote;
|
|
294
|
+
let parenDepth = 0;
|
|
295
|
+
let bracketDepth = 0;
|
|
296
|
+
let inComment = false;
|
|
297
|
+
for (let index = searchStart; index < openIndex; index += 1) {
|
|
298
|
+
const char = css[index];
|
|
299
|
+
const next = css[index + 1];
|
|
300
|
+
const previous = css[index - 1];
|
|
301
|
+
if (char === "\\") {
|
|
302
|
+
index += 1;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (inComment) {
|
|
306
|
+
if (char === "*" && next === "/") {
|
|
307
|
+
inComment = false;
|
|
308
|
+
index += 1;
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (char === "/" && next === "*") {
|
|
313
|
+
inComment = true;
|
|
314
|
+
index += 1;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (quote) {
|
|
318
|
+
if (char === quote && previous !== "\\") {
|
|
319
|
+
quote = undefined;
|
|
320
|
+
}
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (char === "\"" || char === "'") {
|
|
324
|
+
quote = char;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (char === "(") {
|
|
328
|
+
parenDepth += 1;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (char === ")") {
|
|
332
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (char === "[") {
|
|
336
|
+
bracketDepth += 1;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (char === "]") {
|
|
340
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if ((char === "}" || char === ";") && parenDepth === 0 && bracketDepth === 0) {
|
|
344
|
+
boundary = index + 1;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return skipCssIgnorablePrefix(css, boundary, openIndex);
|
|
348
|
+
}
|
|
349
|
+
function skipCssIgnorablePrefix(css, start, end) {
|
|
350
|
+
let index = start;
|
|
351
|
+
while (index < end) {
|
|
352
|
+
while (index < end && /\s/.test(css[index])) {
|
|
353
|
+
index += 1;
|
|
354
|
+
}
|
|
355
|
+
if (css[index] === "/" && css[index + 1] === "*") {
|
|
356
|
+
const commentEnd = css.indexOf("*/", index + 2);
|
|
357
|
+
if (commentEnd === -1 || commentEnd + 2 > end) {
|
|
358
|
+
return start;
|
|
359
|
+
}
|
|
360
|
+
index = commentEnd + 2;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
return index;
|
|
366
|
+
}
|
|
367
|
+
function findNextTopLevelChar(source, target, start) {
|
|
368
|
+
let quote;
|
|
369
|
+
let parenDepth = 0;
|
|
370
|
+
let bracketDepth = 0;
|
|
371
|
+
let inComment = false;
|
|
372
|
+
for (let index = start; index < source.length; index += 1) {
|
|
373
|
+
const char = source[index];
|
|
374
|
+
const next = source[index + 1];
|
|
375
|
+
const previous = source[index - 1];
|
|
376
|
+
if (char === "\\") {
|
|
377
|
+
index += 1;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (inComment) {
|
|
381
|
+
if (char === "*" && next === "/") {
|
|
382
|
+
inComment = false;
|
|
383
|
+
index += 1;
|
|
384
|
+
}
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (char === "/" && next === "*") {
|
|
388
|
+
inComment = true;
|
|
389
|
+
index += 1;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (quote) {
|
|
393
|
+
if (char === quote && previous !== "\\") {
|
|
394
|
+
quote = undefined;
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (char === "\"" || char === "'") {
|
|
399
|
+
quote = char;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (char === "(") {
|
|
403
|
+
parenDepth += 1;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (char === ")") {
|
|
407
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (char === "[") {
|
|
411
|
+
bracketDepth += 1;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (char === "]") {
|
|
415
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (char === target && parenDepth === 0 && bracketDepth === 0) {
|
|
419
|
+
return index;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return -1;
|
|
423
|
+
}
|
|
424
|
+
function findMatchingBrace(source, openIndex) {
|
|
425
|
+
return findMatchingPair(source, openIndex, "{", "}");
|
|
426
|
+
}
|
|
427
|
+
function findMatchingParen(source, openIndex) {
|
|
428
|
+
return findMatchingPair(source, openIndex, "(", ")");
|
|
429
|
+
}
|
|
430
|
+
function findMatchingPair(source, openIndex, openChar, closeChar) {
|
|
431
|
+
let depth = 0;
|
|
432
|
+
let quote;
|
|
433
|
+
let inComment = false;
|
|
434
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
435
|
+
const char = source[index];
|
|
436
|
+
const next = source[index + 1];
|
|
437
|
+
const previous = source[index - 1];
|
|
438
|
+
if (char === "\\") {
|
|
439
|
+
index += 1;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (inComment) {
|
|
443
|
+
if (char === "*" && next === "/") {
|
|
444
|
+
inComment = false;
|
|
445
|
+
index += 1;
|
|
446
|
+
}
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (char === "/" && next === "*") {
|
|
450
|
+
inComment = true;
|
|
451
|
+
index += 1;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (quote) {
|
|
455
|
+
if (char === quote && previous !== "\\") {
|
|
456
|
+
quote = undefined;
|
|
457
|
+
}
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (char === "\"" || char === "'") {
|
|
461
|
+
quote = char;
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (char === openChar) {
|
|
465
|
+
depth += 1;
|
|
466
|
+
}
|
|
467
|
+
else if (char === closeChar) {
|
|
468
|
+
depth -= 1;
|
|
469
|
+
if (depth === 0) {
|
|
470
|
+
return index;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return -1;
|
|
475
|
+
}
|
|
476
|
+
function findUnclosedOpenIndex(source, openIndex, openChar, closeChar) {
|
|
477
|
+
const openIndexes = [];
|
|
478
|
+
let quote;
|
|
479
|
+
let inComment = false;
|
|
480
|
+
for (let index = openIndex; index < source.length; index += 1) {
|
|
481
|
+
const char = source[index];
|
|
482
|
+
const next = source[index + 1];
|
|
483
|
+
const previous = source[index - 1];
|
|
484
|
+
if (char === "\\") {
|
|
485
|
+
index += 1;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (inComment) {
|
|
489
|
+
if (char === "*" && next === "/") {
|
|
490
|
+
inComment = false;
|
|
491
|
+
index += 1;
|
|
492
|
+
}
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
if (char === "/" && next === "*") {
|
|
496
|
+
inComment = true;
|
|
497
|
+
index += 1;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (quote) {
|
|
501
|
+
if (char === quote && previous !== "\\") {
|
|
502
|
+
quote = undefined;
|
|
503
|
+
}
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (char === "\"" || char === "'") {
|
|
507
|
+
quote = char;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (char === openChar) {
|
|
511
|
+
openIndexes.push(index);
|
|
512
|
+
}
|
|
513
|
+
else if (char === closeChar) {
|
|
514
|
+
openIndexes.pop();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return openIndexes.at(-1) ?? openIndex;
|
|
518
|
+
}
|
|
519
|
+
function readAtRuleName(prelude) {
|
|
520
|
+
const match = prelude.match(/^@([-\w]+)/);
|
|
521
|
+
return match?.[1].toLowerCase() ?? "";
|
|
522
|
+
}
|
|
523
|
+
function bodyContainsRules(body) {
|
|
524
|
+
return findNextTopLevelChar(body, "{", 0) !== -1;
|
|
525
|
+
}
|
|
526
|
+
function isCombinator(char) {
|
|
527
|
+
return char === ">" || char === "+" || char === "~" || /\s/.test(char);
|
|
528
|
+
}
|
|
529
|
+
//# sourceMappingURL=css.js.map
|