@gyral/core 0.3.0 → 0.3.2
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 +57 -0
- package/NOTICE +19 -0
- package/README.md +36 -8
- package/dist/call.d.ts +32 -0
- package/dist/call.js +63 -0
- package/dist/call.js.map +1 -0
- package/dist/children.d.ts +25 -3
- package/dist/children.js +18 -1
- package/dist/children.js.map +1 -1
- package/dist/cli/gyral-types.d.ts +2 -0
- package/dist/cli/gyral-types.js +23 -0
- package/dist/cli/gyral-types.js.map +1 -0
- package/dist/command.d.ts +54 -12
- package/dist/command.js +25 -11
- package/dist/command.js.map +1 -1
- package/dist/compiler/ast.d.ts +2 -0
- package/dist/compiler/ast.js +5 -1
- package/dist/compiler/ast.js.map +1 -1
- package/dist/compiler/compile.d.ts +5 -2
- package/dist/compiler/compile.js +36 -17
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/component-entries.d.ts +7 -0
- package/dist/compiler/component-entries.js +42 -0
- package/dist/compiler/component-entries.js.map +1 -0
- package/dist/compiler/components.d.ts +4 -0
- package/dist/compiler/components.js +190 -0
- package/dist/compiler/components.js.map +1 -0
- package/dist/compiler/facts.d.ts +31 -0
- package/dist/compiler/facts.js +285 -0
- package/dist/compiler/facts.js.map +1 -0
- package/dist/compiler/features.d.ts +34 -0
- package/dist/compiler/features.js +158 -0
- package/dist/compiler/features.js.map +1 -0
- package/dist/compiler/hooks.d.ts +4 -0
- package/dist/compiler/hooks.js +144 -89
- package/dist/compiler/hooks.js.map +1 -1
- package/dist/compiler/loader-source.d.ts +14 -0
- package/dist/compiler/loader-source.js +86 -0
- package/dist/compiler/loader-source.js.map +1 -0
- package/dist/compiler/locate.d.ts +14 -0
- package/dist/compiler/locate.js +90 -0
- package/dist/compiler/locate.js.map +1 -0
- package/dist/compiler/parse5-check.d.ts +1 -1
- package/dist/compiler/parse5-check.js +8 -3
- package/dist/compiler/parse5-check.js.map +1 -1
- package/dist/compiler/prop-schemas.d.ts +4 -0
- package/dist/compiler/prop-schemas.js +108 -0
- package/dist/compiler/prop-schemas.js.map +1 -0
- package/dist/compiler/scan.d.ts +13 -3
- package/dist/compiler/scan.js +21 -13
- package/dist/compiler/scan.js.map +1 -1
- package/dist/compiler/scope.d.ts +8 -0
- package/dist/compiler/scope.js +116 -0
- package/dist/compiler/scope.js.map +1 -0
- package/dist/compiler/tag-map.d.ts +41 -0
- package/dist/compiler/tag-map.js +126 -0
- package/dist/compiler/tag-map.js.map +1 -0
- package/dist/components-ready.d.ts +7 -0
- package/dist/components-ready.js +19 -0
- package/dist/components-ready.js.map +1 -0
- package/dist/define.d.ts +12 -4
- package/dist/define.js +8 -12
- package/dist/define.js.map +1 -1
- package/dist/devtools-events.d.ts +8 -0
- package/dist/devtools-events.js.map +1 -1
- package/dist/devtools-off.d.ts +1 -0
- package/dist/devtools-off.js +1 -0
- package/dist/devtools-off.js.map +1 -1
- package/dist/devtools.d.ts +3 -0
- package/dist/devtools.js +14 -0
- package/dist/devtools.js.map +1 -1
- package/dist/drivers-scope.js.map +1 -1
- package/dist/element-internals.d.ts +1 -0
- package/dist/element-internals.js +11 -0
- package/dist/element-internals.js.map +1 -0
- package/dist/element-types.d.ts +7 -4
- package/dist/element-types.js.map +1 -1
- package/dist/element.js +80 -27
- package/dist/element.js.map +1 -1
- package/dist/error-view.d.ts +10 -0
- package/dist/error-view.js +30 -0
- package/dist/error-view.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +47 -0
- package/dist/errors.js.map +1 -0
- package/dist/errors.json +289 -0
- package/dist/eslint/imports.d.ts +2 -0
- package/dist/eslint/imports.js +2 -2
- package/dist/eslint/imports.js.map +1 -1
- package/dist/eslint/index.d.ts +2 -1
- package/dist/eslint/index.js +11 -3
- package/dist/eslint/index.js.map +1 -1
- package/dist/eslint/template-rule.d.ts +1 -0
- package/dist/eslint/template-rule.js +15 -5
- package/dist/eslint/template-rule.js.map +1 -1
- package/dist/eslint/unused-intent-rule.d.ts +2 -0
- package/dist/eslint/unused-intent-rule.js +186 -0
- package/dist/eslint/unused-intent-rule.js.map +1 -0
- package/dist/features.d.ts +2 -2
- package/dist/features.js.map +1 -1
- package/dist/focus.d.ts +6 -0
- package/dist/focus.js +32 -2
- package/dist/focus.js.map +1 -1
- package/dist/form-associated.d.ts +46 -0
- package/dist/form-associated.js +77 -0
- package/dist/form-associated.js.map +1 -0
- package/dist/forms.d.ts +42 -30
- package/dist/forms.js +85 -41
- package/dist/forms.js.map +1 -1
- package/dist/frame-lane.d.ts +11 -0
- package/dist/frame-lane.js +28 -0
- package/dist/frame-lane.js.map +1 -0
- package/dist/head.d.ts +61 -0
- package/dist/head.js +85 -0
- package/dist/head.js.map +1 -0
- package/dist/host-model.d.ts +34 -5
- package/dist/host-model.js +146 -23
- package/dist/host-model.js.map +1 -1
- package/dist/hydration-client.js +2 -2
- package/dist/hydration-client.js.map +1 -1
- package/dist/hydration-loader.d.ts +13 -0
- package/dist/hydration-loader.js +58 -0
- package/dist/hydration-loader.js.map +1 -0
- package/dist/hydration-off.d.ts +8 -0
- package/dist/hydration-off.js +25 -0
- package/dist/hydration-off.js.map +1 -0
- package/dist/hydration.d.ts +0 -12
- package/dist/hydration.js +3 -52
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +21 -24
- package/dist/index.js +10 -19
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-types.d.ts +97 -0
- package/dist/indexeddb-types.js +2 -0
- package/dist/indexeddb-types.js.map +1 -0
- package/dist/indexeddb.d.ts +5 -0
- package/dist/indexeddb.js +118 -0
- package/dist/indexeddb.js.map +1 -0
- package/dist/intent.d.ts +42 -20
- package/dist/intent.js +145 -107
- package/dist/intent.js.map +1 -1
- package/dist/internal/indexeddb-open.d.ts +8 -0
- package/dist/internal/indexeddb-open.js +96 -0
- package/dist/internal/indexeddb-open.js.map +1 -0
- package/dist/internal/indexeddb-transaction.d.ts +4 -0
- package/dist/internal/indexeddb-transaction.js +134 -0
- package/dist/internal/indexeddb-transaction.js.map +1 -0
- package/dist/internal/interpreter.d.ts +12 -4
- package/dist/internal/interpreter.js +36 -48
- package/dist/internal/interpreter.js.map +1 -1
- package/dist/internal/token.d.ts +1 -0
- package/dist/internal/token.js +7 -0
- package/dist/internal/token.js.map +1 -0
- package/dist/internal.d.ts +20 -0
- package/dist/internal.js +22 -0
- package/dist/internal.js.map +1 -0
- package/dist/invoker-fallback-used.d.ts +4 -0
- package/dist/invoker-fallback-used.js +10 -0
- package/dist/invoker-fallback-used.js.map +1 -0
- package/dist/invoker-fallback.d.ts +4 -0
- package/dist/invoker-fallback.js +15 -0
- package/dist/invoker-fallback.js.map +1 -0
- package/dist/light-dom.d.ts +1 -1
- package/dist/light-dom.js.map +1 -1
- package/dist/prop.d.ts +30 -7
- package/dist/prop.js +19 -0
- package/dist/prop.js.map +1 -1
- package/dist/props.d.ts +8 -3
- package/dist/props.js +22 -15
- package/dist/props.js.map +1 -1
- package/dist/retry.d.ts +15 -0
- package/dist/retry.js +48 -0
- package/dist/retry.js.map +1 -0
- package/dist/scheduler.d.ts +7 -3
- package/dist/scheduler.js +38 -56
- package/dist/scheduler.js.map +1 -1
- package/dist/script-json.d.ts +2 -0
- package/dist/script-json.js +13 -0
- package/dist/script-json.js.map +1 -0
- package/dist/server-component.d.ts +1 -1
- package/dist/server-component.js +45 -14
- package/dist/server-component.js.map +1 -1
- package/dist/server.d.ts +9 -3
- package/dist/server.js +12 -3
- package/dist/server.js.map +1 -1
- package/dist/settled.d.ts +4 -3
- package/dist/settled.js +48 -26
- package/dist/settled.js.map +1 -1
- package/dist/spec-features-used.d.ts +21 -0
- package/dist/spec-features-used.js +50 -0
- package/dist/spec-features-used.js.map +1 -0
- package/dist/spec-features.d.ts +28 -0
- package/dist/spec-features.js +42 -0
- package/dist/spec-features.js.map +1 -0
- package/dist/states.d.ts +3 -5
- package/dist/states.js +22 -8
- package/dist/states.js.map +1 -1
- package/dist/storage.d.ts +69 -0
- package/dist/storage.js +158 -0
- package/dist/storage.js.map +1 -0
- package/dist/store-binding.js +2 -2
- package/dist/store-binding.js.map +1 -1
- package/dist/store-scope.d.ts +2 -4
- package/dist/store-scope.js +6 -16
- package/dist/store-scope.js.map +1 -1
- package/dist/store.d.ts +2 -6
- package/dist/store.js +36 -8
- package/dist/store.js.map +1 -1
- package/dist/subscription.d.ts +35 -0
- package/dist/subscription.js +74 -0
- package/dist/subscription.js.map +1 -0
- package/dist/subscriptions.d.ts +26 -0
- package/dist/subscriptions.js +98 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/transitions.d.ts +4 -5
- package/dist/transitions.js +10 -4
- package/dist/transitions.js.map +1 -1
- package/dist/types.d.ts +141 -32
- package/dist/types.js.map +1 -1
- package/dist/use-dialogs.d.ts +2 -0
- package/dist/use-dialogs.js +9 -0
- package/dist/use-dialogs.js.map +1 -0
- package/dist/use-disposal.d.ts +2 -0
- package/dist/use-disposal.js +9 -0
- package/dist/use-disposal.js.map +1 -0
- package/dist/use-error-view.d.ts +2 -0
- package/dist/use-error-view.js +9 -0
- package/dist/use-error-view.js.map +1 -0
- package/dist/use-errored.d.ts +2 -0
- package/dist/use-errored.js +9 -0
- package/dist/use-errored.js.map +1 -0
- package/dist/use-form-associated.d.ts +2 -0
- package/dist/use-form-associated.js +7 -0
- package/dist/use-form-associated.js.map +1 -0
- package/dist/use-frame-lane.d.ts +2 -0
- package/dist/use-frame-lane.js +10 -0
- package/dist/use-frame-lane.js.map +1 -0
- package/dist/use-invokers.d.ts +2 -0
- package/dist/use-invokers.js +10 -0
- package/dist/use-invokers.js.map +1 -0
- package/dist/use-states.d.ts +2 -0
- package/dist/use-states.js +10 -0
- package/dist/use-states.js.map +1 -0
- package/dist/use-subscriptions.d.ts +2 -0
- package/dist/use-subscriptions.js +10 -0
- package/dist/use-subscriptions.js.map +1 -0
- package/dist/use-transitions.d.ts +2 -0
- package/dist/use-transitions.js +10 -0
- package/dist/use-transitions.js.map +1 -0
- package/dist/view/attributes.d.ts +5 -0
- package/dist/view/attributes.js +5 -0
- package/dist/view/attributes.js.map +1 -1
- package/dist/view/compiled.d.ts +1 -1
- package/dist/view/compiled.js +1 -1
- package/dist/view/compiled.js.map +1 -1
- package/dist/view/index.d.ts +9 -4
- package/dist/view/index.js +9 -3
- package/dist/view/index.js.map +1 -1
- package/dist/view/loc.d.ts +9 -0
- package/dist/view/loc.js +50 -0
- package/dist/view/loc.js.map +1 -0
- package/dist/view/message.d.ts +17 -0
- package/dist/view/message.js +40 -0
- package/dist/view/message.js.map +1 -0
- package/dist/view/messages.d.ts +43 -0
- package/dist/view/messages.js +84 -0
- package/dist/view/messages.js.map +1 -0
- package/dist/view/normalize/check.d.ts +5 -2
- package/dist/view/normalize/check.js +6 -3
- package/dist/view/normalize/check.js.map +1 -1
- package/dist/view/normalize/emit.d.ts +2 -0
- package/dist/view/normalize/emit.js +40 -11
- package/dist/view/normalize/emit.js.map +1 -1
- package/dist/view/normalize/errors.js +1 -1
- package/dist/view/normalize/errors.js.map +1 -1
- package/dist/view/normalize/id.d.ts +5 -2
- package/dist/view/normalize/id.js +9 -2
- package/dist/view/normalize/id.js.map +1 -1
- package/dist/view/normalize/normalize.d.ts +7 -4
- package/dist/view/normalize/normalize.js +15 -9
- package/dist/view/normalize/normalize.js.map +1 -1
- package/dist/view/normalize/rules.d.ts +9 -0
- package/dist/view/normalize/rules.js +35 -5
- package/dist/view/normalize/rules.js.map +1 -1
- package/dist/view/normalize/svg.d.ts +14 -0
- package/dist/view/normalize/svg.js +40 -0
- package/dist/view/normalize/svg.js.map +1 -0
- package/dist/view/normalize/tokens.d.ts +5 -1
- package/dist/view/normalize/tokens.js.map +1 -1
- package/dist/view/normalize/tree.d.ts +8 -2
- package/dist/view/normalize/tree.js +58 -13
- package/dist/view/normalize/tree.js.map +1 -1
- package/dist/view/normalize/types.d.ts +24 -4
- package/dist/view/normalize/types.js +4 -1
- package/dist/view/normalize/types.js.map +1 -1
- package/dist/view/normalize/whitespace.js +4 -3
- package/dist/view/normalize/whitespace.js.map +1 -1
- package/dist/view/prepare-stub.js +2 -2
- package/dist/view/prepare-stub.js.map +1 -1
- package/dist/view/prepare.d.ts +5 -2
- package/dist/view/prepare.js +9 -3
- package/dist/view/prepare.js.map +1 -1
- package/dist/view/registry.d.ts +15 -0
- package/dist/view/registry.js +10 -2
- package/dist/view/registry.js.map +1 -1
- package/dist/view/render/adopt-attr.js +7 -1
- package/dist/view/render/adopt-attr.js.map +1 -1
- package/dist/view/render/adopt.js +21 -4
- package/dist/view/render/adopt.js.map +1 -1
- package/dist/view/render/attr-parts.d.ts +12 -1
- package/dist/view/render/attr-parts.js +38 -5
- package/dist/view/render/attr-parts.js.map +1 -1
- package/dist/view/render/child-part.js +5 -2
- package/dist/view/render/child-part.js.map +1 -1
- package/dist/view/render/dispose.d.ts +3 -0
- package/dist/view/render/dispose.js +76 -0
- package/dist/view/render/dispose.js.map +1 -0
- package/dist/view/render/hook-part.d.ts +3 -1
- package/dist/view/render/hook-part.js +21 -5
- package/dist/view/render/hook-part.js.map +1 -1
- package/dist/view/render/hook-run.d.ts +2 -0
- package/dist/view/render/hook-run.js +72 -0
- package/dist/view/render/hook-run.js.map +1 -0
- package/dist/view/render/hooks.d.ts +74 -6
- package/dist/view/render/hooks.js +79 -17
- package/dist/view/render/hooks.js.map +1 -1
- package/dist/view/render/hydrate.js +2 -2
- package/dist/view/render/hydrate.js.map +1 -1
- package/dist/view/render/keyed.d.ts +6 -0
- package/dist/view/render/keyed.js +15 -0
- package/dist/view/render/keyed.js.map +1 -0
- package/dist/view/render/list.d.ts +1 -1
- package/dist/view/render/list.js.map +1 -1
- package/dist/view/render/mismatch.js +5 -3
- package/dist/view/render/mismatch.js.map +1 -1
- package/dist/view/render/modal.d.ts +3 -0
- package/dist/view/render/modal.js +39 -0
- package/dist/view/render/modal.js.map +1 -0
- package/dist/view/render/raw.d.ts +4 -1
- package/dist/view/render/raw.js +6 -2
- package/dist/view/render/raw.js.map +1 -1
- package/dist/view/render/render.d.ts +4 -1
- package/dist/view/render/render.js +17 -12
- package/dist/view/render/render.js.map +1 -1
- package/dist/view/render/toggles.d.ts +6 -0
- package/dist/view/render/toggles.js +6 -0
- package/dist/view/render/toggles.js.map +1 -0
- package/dist/view/render/values.d.ts +1 -1
- package/dist/view/render/values.js.map +1 -1
- package/dist/view/render/warn.d.ts +14 -0
- package/dist/view/render/warn.js +45 -0
- package/dist/view/render/warn.js.map +1 -1
- package/dist/view/server/component.d.ts +6 -3
- package/dist/view/server/component.js +29 -7
- package/dist/view/server/component.js.map +1 -1
- package/dist/view/server/index.d.ts +20 -0
- package/dist/view/server/index.js +12 -4
- package/dist/view/server/index.js.map +1 -1
- package/dist/view/server/tags.d.ts +13 -2
- package/dist/view/server/tags.js +13 -2
- package/dist/view/server/tags.js.map +1 -1
- package/dist/view/server/values.d.ts +2 -0
- package/dist/view/server/values.js +8 -0
- package/dist/view/server/values.js.map +1 -1
- package/dist/view/server/writer.d.ts +9 -4
- package/dist/view/server/writer.js +33 -18
- package/dist/view/server/writer.js.map +1 -1
- package/dist/view/template-element.d.ts +7 -0
- package/dist/view/template-element.js +25 -5
- package/dist/view/template-element.js.map +1 -1
- package/dist/view/template.d.ts +9 -2
- package/dist/view/template.js +61 -8
- package/dist/view/template.js.map +1 -1
- package/dist/view/trusted-html.d.ts +6 -0
- package/dist/view/trusted-html.js +34 -0
- package/dist/view/trusted-html.js.map +1 -0
- package/dist/vite.d.ts +38 -31
- package/dist/vite.js +206 -31
- package/dist/vite.js.map +1 -1
- package/package.json +34 -3
- package/dist/hooks/labelled-by.d.ts +0 -13
- package/dist/hooks/labelled-by.js +0 -51
- package/dist/hooks/labelled-by.js.map +0 -1
- package/dist/random.d.ts +0 -13
- package/dist/random.js +0 -19
- package/dist/random.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Rule 7 at build time with parse5 (view/09-template-rules.md "How rule 7 is checked"): a
|
|
2
2
|
// spec-compliant HTML parser parses each template's HTML as <template> content, exactly like
|
|
3
3
|
// the runtime path's `<template>.innerHTML`, and its tree is compared with the shape the
|
|
4
|
-
// normalizer computed paths for (view/normalize/shape.ts, shared with the browser check).
|
|
4
|
+
// normalizer computed paths for (view/normalize/shape.ts, shared with the browser check). An
|
|
5
|
+
// svg template is parsed inside an <svg>, as the browser path does (template-element.ts).
|
|
5
6
|
//
|
|
6
7
|
// parse5 is an optional peer dependency of @gyral/core, loaded at build time by the compiler
|
|
7
8
|
// only: it never reaches browser or server bundles. Without it the build still runs
|
|
@@ -13,7 +14,7 @@ const isParse5 = (value) => typeof value === 'object' &&
|
|
|
13
14
|
value !== null &&
|
|
14
15
|
typeof value.parseFragment === 'function';
|
|
15
16
|
/**
|
|
16
|
-
* Loads parse5 (`specifier`) as resolved from
|
|
17
|
+
* Loads parse5 (`specifier`) as resolved from `@gyral/core`, else from the app's `root`;
|
|
17
18
|
* undefined when neither has it. `require` (Node loads ES modules with it too) rather than
|
|
18
19
|
* `import()`: when core runs from source, this module is loaded by a Vite module runner that
|
|
19
20
|
* is closed by the time the build starts.
|
|
@@ -47,7 +48,11 @@ export function checkWithParse5(parse5, template, shape) {
|
|
|
47
48
|
// fragment; fragment parsing would drop their <html>/<head>/<body> tags.
|
|
48
49
|
if (template.server)
|
|
49
50
|
return undefined;
|
|
50
|
-
|
|
51
|
+
let nodes = parse5.parseFragment(template.svg ? `<svg>${template.html}</svg>` : template.html).childNodes;
|
|
52
|
+
// An svg template: the <svg>'s children, plus anything the parser moved out of it.
|
|
53
|
+
if (template.svg && nodes !== undefined)
|
|
54
|
+
nodes = [...(nodes[0]?.childNodes ?? []), ...nodes.slice(1)];
|
|
55
|
+
const problem = shapeMismatch(shape, toShape(nodes ?? []));
|
|
51
56
|
return problem === undefined
|
|
52
57
|
? undefined
|
|
53
58
|
: repairError(template, 'The HTML parser (parse5)', problem);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse5-check.js","sourceRoot":"","sources":["../../src/compiler/parse5-check.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,6FAA6F;AAC7F,yFAAyF;AACzF,0FAA0F;AAC1F,EAAE;AACF,6FAA6F;AAC7F,oFAAoF;AACpF,+DAA+D;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,WAAW,EACX,aAAa,GAId,MAAM,kBAAkB,CAAC;AAc1B,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CACnD,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAAqC,CAAC,aAAa,KAAK,UAAU,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,IAAY;IACxD,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,aAAa,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,QAAQ,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,KAA4B;IAC3C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAa,EAAE;QACnC,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC;QAC9C,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;YAAE,OAAO,UAAU,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QACvF,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,QAAwB,EACxB,KAAY;IAEZ,oFAAoF;IACpF,yEAAyE;IACzE,IAAI,QAAQ,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACtC,
|
|
1
|
+
{"version":3,"file":"parse5-check.js","sourceRoot":"","sources":["../../src/compiler/parse5-check.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,6FAA6F;AAC7F,yFAAyF;AACzF,6FAA6F;AAC7F,0FAA0F;AAC1F,EAAE;AACF,6FAA6F;AAC7F,oFAAoF;AACpF,+DAA+D;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,WAAW,EACX,aAAa,GAId,MAAM,kBAAkB,CAAC;AAc1B,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CACnD,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAAqC,CAAC,aAAa,KAAK,UAAU,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,IAAY;IACxD,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,aAAa,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,QAAQ,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAM,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,KAA4B;IAC3C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAa,EAAE;QACnC,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC;QAC9C,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;YAAE,OAAO,UAAU,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QACvF,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,QAAwB,EACxB,KAAY;IAEZ,oFAAoF;IACpF,yEAAyE;IACzE,IAAI,QAAQ,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACtC,IAAI,KAAK,GAAG,MAAM,CAAC,aAAa,CAC9B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAC7D,CAAC,UAAU,CAAC;IACb,mFAAmF;IACnF,IAAI,QAAQ,CAAC,GAAG,IAAI,KAAK,KAAK,SAAS;QACrC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,0BAA0B,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC","sourcesContent":["// Rule 7 at build time with parse5 (view/09-template-rules.md \"How rule 7 is checked\"): a\n// spec-compliant HTML parser parses each template's HTML as <template> content, exactly like\n// the runtime path's `<template>.innerHTML`, and its tree is compared with the shape the\n// normalizer computed paths for (view/normalize/shape.ts, shared with the browser check). An\n// svg template is parsed inside an <svg>, as the browser path does (template-element.ts).\n//\n// parse5 is an optional peer dependency of @gyral/core, loaded at build time by the compiler\n// only: it never reaches browser or server bundles. Without it the build still runs\n// the normalizer's structural rule 7 check and skips this one.\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport {\n repairError,\n shapeMismatch,\n type Shape,\n type ShapeNode,\n type TemplateObject,\n} from '../view/index.js';\n\n/** The slice of parse5's API the check uses (default tree adapter). */\nexport interface Parse5 {\n parseFragment(html: string): Parse5Node;\n}\n\ninterface Parse5Node {\n readonly nodeName: string;\n readonly tagName?: string;\n readonly childNodes?: readonly Parse5Node[];\n readonly content?: Parse5Node;\n}\n\nconst isParse5 = (value: unknown): value is Parse5 =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { parseFragment?: unknown }).parseFragment === 'function';\n\n/**\n * Loads parse5 (`specifier`) as resolved from `@gyral/core`, else from the app's `root`;\n * undefined when neither has it. `require` (Node loads ES modules with it too) rather than\n * `import()`: when core runs from source, this module is loaded by a Vite module runner that\n * is closed by the time the build starts.\n */\nexport function loadParse5(specifier: string, root: string): Parse5 | undefined {\n for (const base of [import.meta.url, join(root, 'package.json')]) {\n try {\n const module: unknown = createRequire(base)(specifier);\n if (isParse5(module)) return module;\n } catch {\n // not resolvable from here\n }\n }\n return undefined;\n}\n\nfunction toShape(nodes: readonly Parse5Node[]): Shape {\n return nodes.map((node): ShapeNode => {\n if (node.nodeName === '#text') return '#text';\n if (node.nodeName === '#comment') return '#comment';\n const inner = node.tagName === 'template' ? node.content?.childNodes : node.childNodes;\n return [(node.tagName ?? node.nodeName).toLowerCase(), toShape(inner ?? [])];\n });\n}\n\n/** Rule 7's error when parse5 builds a different tree than `shape`; undefined if it agrees. */\nexport function checkWithParse5(\n parse5: Parse5,\n template: TemplateObject,\n shape: Shape,\n): Error | undefined {\n // Page shells (document-level tags) are written by the server and never parsed as a\n // fragment; fragment parsing would drop their <html>/<head>/<body> tags.\n if (template.server) return undefined;\n let nodes = parse5.parseFragment(\n template.svg ? `<svg>${template.html}</svg>` : template.html,\n ).childNodes;\n // An svg template: the <svg>'s children, plus anything the parser moved out of it.\n if (template.svg && nodes !== undefined)\n nodes = [...(nodes[0]?.childNodes ?? []), ...nodes.slice(1)];\n const problem = shapeMismatch(shape, toShape(nodes ?? []));\n return problem === undefined\n ? undefined\n : repairError(template, 'The HTML parser (parse5)', problem);\n}\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Property-only prop schemas out of production client builds (gyral-c5d.14, view/05-element.md
|
|
2
|
+
// "When props are validated"): `prop.value(check, opts)` declares a prop with no attribute, and
|
|
3
|
+
// production never checks property sets (or seeds), so its schema or type guard can't run
|
|
4
|
+
// there. Replacing a check that is a plain reference (`prop.value(Settings)`, `schemas.settings`) or an
|
|
5
|
+
// inline function (`prop.value((u): u is Settings => …)`) with `void 0` lets the bundler drop it and
|
|
6
|
+
// whatever only it used, e.g. a schema module (when the bundler can: Rolldown in Vite 8.3 keeps
|
|
7
|
+
// side-effect-free schema builders in a lazy chunk once their library is in a chunk shared with
|
|
8
|
+
// the entry; view/05 "When props are validated"). Calls (`prop.value(v.array(Item))`) stay: their
|
|
9
|
+
// evaluation could have effects, and this pass never changes what runs. `prop.json` keeps its
|
|
10
|
+
// check (attributes are parsed through it in production). Development builds keep everything:
|
|
11
|
+
// they validate property sets.
|
|
12
|
+
import { children, nodeAt, nodesAt, scopeNames } from './ast.js';
|
|
13
|
+
/** Where `prop` comes from: the documented import only (`import { prop } from '@gyral/core'`). */
|
|
14
|
+
const CORE = '@gyral/core';
|
|
15
|
+
/** TypeScript wrappers around a runtime expression (`x as T`, `x satisfies T`, `x!`). */
|
|
16
|
+
const WRAPPERS = new Set(['TSAsExpression', 'TSSatisfiesExpression', 'TSNonNullExpression']);
|
|
17
|
+
/** Expressions whose evaluation has no effect: dropping them changes nothing that runs. */
|
|
18
|
+
function droppable(node) {
|
|
19
|
+
if (node === undefined)
|
|
20
|
+
return false;
|
|
21
|
+
if (WRAPPERS.has(node.type))
|
|
22
|
+
return droppable(nodeAt(node, 'expression'));
|
|
23
|
+
switch (node.type) {
|
|
24
|
+
case 'Identifier':
|
|
25
|
+
case 'ArrowFunctionExpression':
|
|
26
|
+
case 'FunctionExpression':
|
|
27
|
+
return true;
|
|
28
|
+
case 'MemberExpression': {
|
|
29
|
+
// `schemas.settings`: a chain of plain names (no computed keys, no calls, no optional links).
|
|
30
|
+
const object = nodeAt(node, 'object');
|
|
31
|
+
return (node['computed'] !== true &&
|
|
32
|
+
node['optional'] !== true &&
|
|
33
|
+
(object?.type === 'Identifier' ||
|
|
34
|
+
(object?.type === 'MemberExpression' && droppable(object))));
|
|
35
|
+
}
|
|
36
|
+
default:
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The names `prop` is imported as: `{ local }` or, for `import * as ns`, `{ ns }`. */
|
|
41
|
+
function propImports(program) {
|
|
42
|
+
const locals = new Set();
|
|
43
|
+
const ns = new Set();
|
|
44
|
+
for (const statement of nodesAt(program, 'body')) {
|
|
45
|
+
if (statement.type !== 'ImportDeclaration' || statement['importKind'] === 'type')
|
|
46
|
+
continue;
|
|
47
|
+
if (nodeAt(statement, 'source')?.['value'] !== CORE)
|
|
48
|
+
continue;
|
|
49
|
+
for (const s of nodesAt(statement, 'specifiers')) {
|
|
50
|
+
const local = nodeAt(s, 'local')?.['name'];
|
|
51
|
+
if (typeof local !== 'string' || s['importKind'] === 'type')
|
|
52
|
+
continue;
|
|
53
|
+
if (s.type === 'ImportNamespaceSpecifier')
|
|
54
|
+
ns.add(local);
|
|
55
|
+
const imported = nodeAt(s, 'imported');
|
|
56
|
+
if (s.type === 'ImportSpecifier' && (imported?.['name'] ?? imported?.['value']) === 'prop') {
|
|
57
|
+
locals.add(local);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { locals, ns };
|
|
62
|
+
}
|
|
63
|
+
/** Edits that replace the droppable first argument of each `prop.value(…)` call with `void 0`. */
|
|
64
|
+
export function stripPropSchemas(program, code) {
|
|
65
|
+
const { locals, ns } = propImports(program);
|
|
66
|
+
if (locals.size === 0 && ns.size === 0)
|
|
67
|
+
return [];
|
|
68
|
+
const edits = [];
|
|
69
|
+
const scopes = [];
|
|
70
|
+
const visible = (name, names) => typeof name === 'string' && names.has(name) && !scopes.some((s) => s.has(name));
|
|
71
|
+
/** `prop.value` (or `ns.prop.value`) with `prop`/`ns` the imported binding. */
|
|
72
|
+
const isPropValue = (callee) => {
|
|
73
|
+
if (callee?.type !== 'MemberExpression' || callee['computed'] === true)
|
|
74
|
+
return false;
|
|
75
|
+
if (nodeAt(callee, 'property')?.['name'] !== 'value')
|
|
76
|
+
return false;
|
|
77
|
+
const object = nodeAt(callee, 'object');
|
|
78
|
+
if (object?.type === 'Identifier')
|
|
79
|
+
return visible(object['name'], locals);
|
|
80
|
+
if (object?.type !== 'MemberExpression' || object['computed'] === true)
|
|
81
|
+
return false;
|
|
82
|
+
const base = nodeAt(object, 'object');
|
|
83
|
+
return (nodeAt(object, 'property')?.['name'] === 'prop' &&
|
|
84
|
+
base?.type === 'Identifier' &&
|
|
85
|
+
visible(base['name'], ns));
|
|
86
|
+
};
|
|
87
|
+
const walk = (node) => {
|
|
88
|
+
const declared = scopeNames(node)?.filter((name) => locals.has(name) || ns.has(name));
|
|
89
|
+
if (declared !== undefined && declared.length > 0)
|
|
90
|
+
scopes.push(new Set(declared));
|
|
91
|
+
if (node.type === 'CallExpression' && isPropValue(nodeAt(node, 'callee'))) {
|
|
92
|
+
const [check] = nodesAt(node, 'arguments');
|
|
93
|
+
if (check !== undefined && droppable(check)) {
|
|
94
|
+
// Keep the line count, so every following line keeps its number.
|
|
95
|
+
const lines = code.slice(check.start, check.end).split('\n').length - 1;
|
|
96
|
+
edits.push({ start: check.start, end: check.end, text: `void 0${'\n'.repeat(lines)}` });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const child of children(node))
|
|
100
|
+
walk(child);
|
|
101
|
+
if (declared !== undefined && declared.length > 0)
|
|
102
|
+
scopes.pop();
|
|
103
|
+
};
|
|
104
|
+
for (const statement of nodesAt(program, 'body'))
|
|
105
|
+
walk(statement);
|
|
106
|
+
return edits;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=prop-schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prop-schemas.js","sourceRoot":"","sources":["../../src/compiler/prop-schemas.ts"],"names":[],"mappings":"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","sourcesContent":["// Property-only prop schemas out of production client builds (gyral-c5d.14, view/05-element.md\n// \"When props are validated\"): `prop.value(check, opts)` declares a prop with no attribute, and\n// production never checks property sets (or seeds), so its schema or type guard can't run\n// there. Replacing a check that is a plain reference (`prop.value(Settings)`, `schemas.settings`) or an\n// inline function (`prop.value((u): u is Settings => …)`) with `void 0` lets the bundler drop it and\n// whatever only it used, e.g. a schema module (when the bundler can: Rolldown in Vite 8.3 keeps\n// side-effect-free schema builders in a lazy chunk once their library is in a chunk shared with\n// the entry; view/05 \"When props are validated\"). Calls (`prop.value(v.array(Item))`) stay: their\n// evaluation could have effects, and this pass never changes what runs. `prop.json` keeps its\n// check (attributes are parsed through it in production). Development builds keep everything:\n// they validate property sets.\nimport { children, nodeAt, nodesAt, scopeNames, type Node } from './ast.js';\nimport type { Edit } from './source.js';\n\n/** Where `prop` comes from: the documented import only (`import { prop } from '@gyral/core'`). */\nconst CORE = '@gyral/core';\n\n/** TypeScript wrappers around a runtime expression (`x as T`, `x satisfies T`, `x!`). */\nconst WRAPPERS = new Set(['TSAsExpression', 'TSSatisfiesExpression', 'TSNonNullExpression']);\n\n/** Expressions whose evaluation has no effect: dropping them changes nothing that runs. */\nfunction droppable(node: Node | undefined): boolean {\n if (node === undefined) return false;\n if (WRAPPERS.has(node.type)) return droppable(nodeAt(node, 'expression'));\n switch (node.type) {\n case 'Identifier':\n case 'ArrowFunctionExpression':\n case 'FunctionExpression':\n return true;\n case 'MemberExpression': {\n // `schemas.settings`: a chain of plain names (no computed keys, no calls, no optional links).\n const object = nodeAt(node, 'object');\n return (\n node['computed'] !== true &&\n node['optional'] !== true &&\n (object?.type === 'Identifier' ||\n (object?.type === 'MemberExpression' && droppable(object)))\n );\n }\n default:\n return false;\n }\n}\n\n/** The names `prop` is imported as: `{ local }` or, for `import * as ns`, `{ ns }`. */\nfunction propImports(program: Node): { readonly locals: Set<string>; readonly ns: Set<string> } {\n const locals = new Set<string>();\n const ns = new Set<string>();\n for (const statement of nodesAt(program, 'body')) {\n if (statement.type !== 'ImportDeclaration' || statement['importKind'] === 'type') continue;\n if (nodeAt(statement, 'source')?.['value'] !== CORE) continue;\n for (const s of nodesAt(statement, 'specifiers')) {\n const local = nodeAt(s, 'local')?.['name'];\n if (typeof local !== 'string' || s['importKind'] === 'type') continue;\n if (s.type === 'ImportNamespaceSpecifier') ns.add(local);\n const imported = nodeAt(s, 'imported');\n if (s.type === 'ImportSpecifier' && (imported?.['name'] ?? imported?.['value']) === 'prop') {\n locals.add(local);\n }\n }\n }\n return { locals, ns };\n}\n\n/** Edits that replace the droppable first argument of each `prop.value(…)` call with `void 0`. */\nexport function stripPropSchemas(program: Node, code: string): Edit[] {\n const { locals, ns } = propImports(program);\n if (locals.size === 0 && ns.size === 0) return [];\n const edits: Edit[] = [];\n const scopes: Set<string>[] = [];\n const visible = (name: unknown, names: Set<string>): boolean =>\n typeof name === 'string' && names.has(name) && !scopes.some((s) => s.has(name));\n\n /** `prop.value` (or `ns.prop.value`) with `prop`/`ns` the imported binding. */\n const isPropValue = (callee: Node | undefined): boolean => {\n if (callee?.type !== 'MemberExpression' || callee['computed'] === true) return false;\n if (nodeAt(callee, 'property')?.['name'] !== 'value') return false;\n const object = nodeAt(callee, 'object');\n if (object?.type === 'Identifier') return visible(object['name'], locals);\n if (object?.type !== 'MemberExpression' || object['computed'] === true) return false;\n const base = nodeAt(object, 'object');\n return (\n nodeAt(object, 'property')?.['name'] === 'prop' &&\n base?.type === 'Identifier' &&\n visible(base['name'], ns)\n );\n };\n\n const walk = (node: Node): void => {\n const declared = scopeNames(node)?.filter((name) => locals.has(name) || ns.has(name));\n if (declared !== undefined && declared.length > 0) scopes.push(new Set(declared));\n if (node.type === 'CallExpression' && isPropValue(nodeAt(node, 'callee'))) {\n const [check] = nodesAt(node, 'arguments');\n if (check !== undefined && droppable(check)) {\n // Keep the line count, so every following line keeps its number.\n const lines = code.slice(check.start, check.end).split('\\n').length - 1;\n edits.push({ start: check.start, end: check.end, text: `void 0${'\\n'.repeat(lines)}` });\n }\n }\n for (const child of children(node)) walk(child);\n if (declared !== undefined && declared.length > 0) scopes.pop();\n };\n for (const statement of nodesAt(program, 'body')) walk(statement);\n return edits;\n}\n"]}
|
package/dist/compiler/scan.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { type Node } from './ast.js';
|
|
2
|
-
/**
|
|
2
|
+
/** The template tags the compiler rewrites. */
|
|
3
|
+
export type Tag = 'html' | 'svg';
|
|
4
|
+
/**
|
|
5
|
+
* `import { html as local } from 'specifier'` (`tag` says which tag, html or svg), or
|
|
6
|
+
* `import * as local from 'specifier'` (no `tag`: `ns.html` and `ns.svg` both are).
|
|
7
|
+
*/
|
|
3
8
|
export interface HtmlImport {
|
|
4
9
|
readonly local: string;
|
|
5
10
|
readonly namespace: boolean;
|
|
6
11
|
readonly specifier: string;
|
|
12
|
+
readonly tag?: Tag;
|
|
7
13
|
}
|
|
8
14
|
export interface Imports {
|
|
9
15
|
readonly imports: readonly HtmlImport[];
|
|
@@ -14,10 +20,11 @@ export interface Imports {
|
|
|
14
20
|
}[];
|
|
15
21
|
}
|
|
16
22
|
export interface Uses {
|
|
17
|
-
/** TaggedTemplateExpression nodes whose tag is a matching binding. */
|
|
23
|
+
/** TaggedTemplateExpression nodes whose tag is a matching binding, and which tag it is. */
|
|
18
24
|
readonly sites: readonly {
|
|
19
25
|
readonly node: Node;
|
|
20
26
|
readonly binding: HtmlImport;
|
|
27
|
+
readonly tag: Tag;
|
|
21
28
|
}[];
|
|
22
29
|
/** Other references; `exported`: an `export { html }` specifier. */
|
|
23
30
|
readonly leftovers: readonly {
|
|
@@ -25,7 +32,10 @@ export interface Uses {
|
|
|
25
32
|
readonly exported: boolean;
|
|
26
33
|
}[];
|
|
27
34
|
}
|
|
28
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Imports of `html` and `svg` (named) and namespace imports, plus `export { html } from`
|
|
37
|
+
* re-exports (of either tag).
|
|
38
|
+
*/
|
|
29
39
|
export declare function htmlImports(program: Node): Imports;
|
|
30
40
|
/** Every use of `bindings` in `program`, skipping names that a nested scope redeclares. */
|
|
31
41
|
export declare function findUses(program: Node, bindings: readonly HtmlImport[]): Uses;
|
package/dist/compiler/scan.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
// Finds a module's `html` call sites for the template compiler (view/01-templates.md
|
|
1
|
+
// Finds a module's `html` and `svg` call sites for the template compiler (view/01-templates.md
|
|
2
2
|
// "Compiled"). Two steps, because matching an import against the configured sources may need
|
|
3
3
|
// module resolution (async, in the plugin): `htmlImports` lists the candidate imports, and
|
|
4
4
|
// `findUses` walks the module, scope-aware, for every reference to the matching bindings:
|
|
5
|
-
// - html`…` (or ns.html`…` for `import * as ns`) is a call site
|
|
5
|
+
// - html`…` (or ns.html`…` for `import * as ns`) is a call site, and so is svg`…`;
|
|
6
6
|
// - any other reference (`const h = html`, `html(strings)`, `ns[x]`, `{ html } = ns`) is a
|
|
7
7
|
// leftover the compiler can't follow: an error, since it would reach the runtime preparer;
|
|
8
8
|
// - `export { html }` re-exports it: a warning here, an error in generateBundle when an
|
|
9
|
-
// uncompiled `html` survives in the bundle.
|
|
9
|
+
// uncompiled `html` survives in the bundle. Everything said of `html` holds for `svg`.
|
|
10
10
|
import { children, nodeAt, nodesAt, scopeNames } from './ast.js';
|
|
11
|
+
const TAGS = ['html', 'svg'];
|
|
12
|
+
const tagOf = (name) => name !== undefined && TAGS.includes(name) ? name : undefined;
|
|
11
13
|
const nameOf = (node) => node === undefined
|
|
12
14
|
? undefined
|
|
13
15
|
: node.type === 'Identifier'
|
|
@@ -17,7 +19,10 @@ const nameOf = (node) => node === undefined
|
|
|
17
19
|
: undefined;
|
|
18
20
|
const specifierOf = (node) => nameOf(nodeAt(node, 'source'));
|
|
19
21
|
const isValue = (node) => node['importKind'] !== 'type' && node['exportKind'] !== 'type';
|
|
20
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Imports of `html` and `svg` (named) and namespace imports, plus `export { html } from`
|
|
24
|
+
* re-exports (of either tag).
|
|
25
|
+
*/
|
|
21
26
|
export function htmlImports(program) {
|
|
22
27
|
const imports = [];
|
|
23
28
|
const reexports = [];
|
|
@@ -33,13 +38,15 @@ export function htmlImports(program) {
|
|
|
33
38
|
if (s.type === 'ImportNamespaceSpecifier') {
|
|
34
39
|
imports.push({ local, namespace: true, specifier });
|
|
35
40
|
}
|
|
36
|
-
else if (s.type === 'ImportSpecifier'
|
|
37
|
-
|
|
41
|
+
else if (s.type === 'ImportSpecifier') {
|
|
42
|
+
const tag = tagOf(nameOf(nodeAt(s, 'imported')));
|
|
43
|
+
if (tag !== undefined)
|
|
44
|
+
imports.push({ local, namespace: false, specifier, tag });
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
else if (statement.type === 'ExportNamedDeclaration') {
|
|
42
|
-
const html = nodesAt(statement, 'specifiers').find((s) => isValue(s) && nameOf(nodeAt(s, 'local'))
|
|
49
|
+
const html = nodesAt(statement, 'specifiers').find((s) => isValue(s) && tagOf(nameOf(nodeAt(s, 'local'))) !== undefined);
|
|
43
50
|
if (html !== undefined)
|
|
44
51
|
reexports.push({ node: html, specifier });
|
|
45
52
|
}
|
|
@@ -59,16 +66,17 @@ export function findUses(program, bindings) {
|
|
|
59
66
|
return undefined;
|
|
60
67
|
return byName.get(name);
|
|
61
68
|
};
|
|
62
|
-
/** The binding a tag refers to: `html` itself, or `ns.html
|
|
69
|
+
/** The binding a tag refers to, and which tag: `html` itself, or `ns.html` (or svg). */
|
|
63
70
|
const tagBinding = (tag) => {
|
|
64
71
|
if (tag?.type === 'Identifier') {
|
|
65
72
|
const b = binding(tag);
|
|
66
|
-
return b?.
|
|
73
|
+
return b?.tag === undefined ? undefined : { binding: b, tag: b.tag };
|
|
67
74
|
}
|
|
68
75
|
if (tag?.type !== 'MemberExpression' || tag['computed'] === true)
|
|
69
76
|
return undefined;
|
|
70
77
|
const b = binding(nodeAt(tag, 'object'));
|
|
71
|
-
|
|
78
|
+
const name = tagOf(nameOf(nodeAt(tag, 'property')));
|
|
79
|
+
return b?.namespace === true && name !== undefined ? { binding: b, tag: name } : undefined;
|
|
72
80
|
};
|
|
73
81
|
const walkAll = (nodes) => {
|
|
74
82
|
for (const n of nodes)
|
|
@@ -96,7 +104,7 @@ export function findUses(program, bindings) {
|
|
|
96
104
|
const b = tagBinding(tag);
|
|
97
105
|
if (b === undefined)
|
|
98
106
|
break;
|
|
99
|
-
sites.push({ node,
|
|
107
|
+
sites.push({ node, ...b });
|
|
100
108
|
walkAll(nodesAt(nodeAt(node, 'quasi') ?? node, 'expressions'));
|
|
101
109
|
return;
|
|
102
110
|
}
|
|
@@ -105,8 +113,8 @@ export function findUses(program, bindings) {
|
|
|
105
113
|
const b = binding(object);
|
|
106
114
|
const computed = node['computed'] === true;
|
|
107
115
|
if (b !== undefined) {
|
|
108
|
-
// ns.other is fine; ns.html outside a tag, or ns[expr], can't be followed.
|
|
109
|
-
if (!b.namespace || computed || nameOf(nodeAt(node, 'property'))
|
|
116
|
+
// ns.other is fine; ns.html (or ns.svg) outside a tag, or ns[expr], can't be followed.
|
|
117
|
+
if (!b.namespace || computed || tagOf(nameOf(nodeAt(node, 'property'))) !== undefined) {
|
|
110
118
|
leftovers.push({ node, exported: false });
|
|
111
119
|
}
|
|
112
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/compiler/scan.ts"],"names":[],"mappings":"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","sourcesContent":["// Finds a module's `html` call sites for the template compiler (view/01-templates.md\n// \"Compiled\"). Two steps, because matching an import against the configured sources may need\n// module resolution (async, in the plugin): `htmlImports` lists the candidate imports, and\n// `findUses` walks the module, scope-aware, for every reference to the matching bindings:\n// - html`…` (or ns.html`…` for `import * as ns`) is a call site;\n// - any other reference (`const h = html`, `html(strings)`, `ns[x]`, `{ html } = ns`) is a\n// leftover the compiler can't follow: an error, since it would reach the runtime preparer;\n// - `export { html }` re-exports it: a warning here, an error in generateBundle when an\n// uncompiled `html` survives in the bundle.\nimport { children, nodeAt, nodesAt, scopeNames, type Node } from './ast.js';\n\n/** `import { html as local } from 'specifier'`, or `import * as local from 'specifier'`. */\nexport interface HtmlImport {\n readonly local: string;\n readonly namespace: boolean;\n readonly specifier: string;\n}\n\nexport interface Imports {\n readonly imports: readonly HtmlImport[];\n /** `export { html } from 'specifier'` (its node, for a code frame). */\n readonly reexports: readonly { readonly node: Node; readonly specifier: string }[];\n}\n\nexport interface Uses {\n /** TaggedTemplateExpression nodes whose tag is a matching binding. */\n readonly sites: readonly { readonly node: Node; readonly binding: HtmlImport }[];\n /** Other references; `exported`: an `export { html }` specifier. */\n readonly leftovers: readonly { readonly node: Node; readonly exported: boolean }[];\n}\n\nconst nameOf = (node: Node | undefined): string | undefined =>\n node === undefined\n ? undefined\n : node.type === 'Identifier'\n ? (node['name'] as string)\n : node.type === 'Literal' && typeof node['value'] === 'string'\n ? node['value']\n : undefined;\n\nconst specifierOf = (node: Node): string | undefined => nameOf(nodeAt(node, 'source'));\n\nconst isValue = (node: Node): boolean =>\n node['importKind'] !== 'type' && node['exportKind'] !== 'type';\n\n/** Imports of `html` (named) and namespace imports, plus `export { html } from` re-exports. */\nexport function htmlImports(program: Node): Imports {\n const imports: HtmlImport[] = [];\n const reexports: { node: Node; specifier: string }[] = [];\n for (const statement of nodesAt(program, 'body')) {\n const specifier = specifierOf(statement);\n if (specifier === undefined || !isValue(statement)) continue;\n if (statement.type === 'ImportDeclaration') {\n for (const s of nodesAt(statement, 'specifiers')) {\n const local = nameOf(nodeAt(s, 'local'));\n if (local === undefined || !isValue(s)) continue;\n if (s.type === 'ImportNamespaceSpecifier') {\n imports.push({ local, namespace: true, specifier });\n } else if (s.type === 'ImportSpecifier' && nameOf(nodeAt(s, 'imported')) === 'html') {\n imports.push({ local, namespace: false, specifier });\n }\n }\n } else if (statement.type === 'ExportNamedDeclaration') {\n const html = nodesAt(statement, 'specifiers').find(\n (s) => isValue(s) && nameOf(nodeAt(s, 'local')) === 'html',\n );\n if (html !== undefined) reexports.push({ node: html, specifier });\n }\n }\n return { imports, reexports };\n}\n\nconst unparen = (node: Node | undefined): Node | undefined =>\n node?.type === 'ParenthesizedExpression' ? unparen(nodeAt(node, 'expression')) : node;\n\n/** Every use of `bindings` in `program`, skipping names that a nested scope redeclares. */\nexport function findUses(program: Node, bindings: readonly HtmlImport[]): Uses {\n const byName = new Map(bindings.map((b) => [b.local, b]));\n const sites: { node: Node; binding: HtmlImport }[] = [];\n const leftovers: { node: Node; exported: boolean }[] = [];\n const scopes: Set<string>[] = [];\n\n const binding = (node: Node | undefined): HtmlImport | undefined => {\n const name = node?.type === 'Identifier' ? (node['name'] as string) : undefined;\n if (name === undefined || scopes.some((s) => s.has(name))) return undefined;\n return byName.get(name);\n };\n\n /** The binding a tag refers to: `html` itself, or `ns.html`. */\n const tagBinding = (tag: Node | undefined): HtmlImport | undefined => {\n if (tag?.type === 'Identifier') {\n const b = binding(tag);\n return b?.namespace === false ? b : undefined;\n }\n if (tag?.type !== 'MemberExpression' || tag['computed'] === true) return undefined;\n const b = binding(nodeAt(tag, 'object'));\n return b?.namespace === true && nameOf(nodeAt(tag, 'property')) === 'html' ? b : undefined;\n };\n\n const walkAll = (nodes: readonly Node[]): void => {\n for (const n of nodes) walk(n);\n };\n\n function visit(node: Node): void {\n switch (node.type) {\n case 'ImportDeclaration':\n case 'ExportAllDeclaration':\n case 'MetaProperty':\n case 'BreakStatement':\n case 'ContinueStatement':\n return;\n case 'ExportNamedDeclaration':\n if (nodeAt(node, 'source') === undefined) {\n for (const s of nodesAt(node, 'specifiers')) {\n if (binding(nodeAt(s, 'local')) !== undefined)\n leftovers.push({ node: s, exported: true });\n }\n }\n walkAll(nodesAt(node, 'declaration'));\n return;\n case 'TaggedTemplateExpression': {\n const tag = unparen(nodeAt(node, 'tag'));\n const b = tagBinding(tag);\n if (b === undefined) break;\n sites.push({ node, binding: b });\n walkAll(nodesAt(nodeAt(node, 'quasi') ?? node, 'expressions'));\n return;\n }\n case 'MemberExpression': {\n const object = nodeAt(node, 'object');\n const b = binding(object);\n const computed = node['computed'] === true;\n if (b !== undefined) {\n // ns.other is fine; ns.html outside a tag, or ns[expr], can't be followed.\n if (!b.namespace || computed || nameOf(nodeAt(node, 'property')) === 'html') {\n leftovers.push({ node, exported: false });\n }\n } else walkAll(nodesAt(node, 'object'));\n if (computed) walkAll(nodesAt(node, 'property'));\n return;\n }\n case 'Property':\n case 'MethodDefinition':\n case 'PropertyDefinition':\n case 'AccessorProperty':\n if (node['computed'] === true) walkAll(nodesAt(node, 'key'));\n walkAll(nodesAt(node, 'decorators'));\n walkAll(nodesAt(node, 'value'));\n return;\n case 'LabeledStatement':\n walkAll(nodesAt(node, 'body'));\n return;\n case 'Identifier':\n if (binding(node) !== undefined) leftovers.push({ node, exported: false });\n return;\n }\n walkAll(children(node));\n }\n\n function walk(node: Node): void {\n const declared = scopeNames(node)?.filter((name) => byName.has(name));\n if (declared === undefined || declared.length === 0) {\n visit(node);\n return;\n }\n scopes.push(new Set(declared));\n visit(node);\n scopes.pop();\n }\n\n if (byName.size > 0) walkAll(nodesAt(program, 'body'));\n return { sites, leftovers };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/compiler/scan.ts"],"names":[],"mappings":"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","sourcesContent":["// Finds a module's `html` and `svg` call sites for the template compiler (view/01-templates.md\n// \"Compiled\"). Two steps, because matching an import against the configured sources may need\n// module resolution (async, in the plugin): `htmlImports` lists the candidate imports, and\n// `findUses` walks the module, scope-aware, for every reference to the matching bindings:\n// - html`…` (or ns.html`…` for `import * as ns`) is a call site, and so is svg`…`;\n// - any other reference (`const h = html`, `html(strings)`, `ns[x]`, `{ html } = ns`) is a\n// leftover the compiler can't follow: an error, since it would reach the runtime preparer;\n// - `export { html }` re-exports it: a warning here, an error in generateBundle when an\n// uncompiled `html` survives in the bundle. Everything said of `html` holds for `svg`.\nimport { children, nodeAt, nodesAt, scopeNames, type Node } from './ast.js';\n\n/** The template tags the compiler rewrites. */\nexport type Tag = 'html' | 'svg';\nconst TAGS: readonly string[] = ['html', 'svg'];\nconst tagOf = (name: string | undefined): Tag | undefined =>\n name !== undefined && TAGS.includes(name) ? (name as Tag) : undefined;\n\n/**\n * `import { html as local } from 'specifier'` (`tag` says which tag, html or svg), or\n * `import * as local from 'specifier'` (no `tag`: `ns.html` and `ns.svg` both are).\n */\nexport interface HtmlImport {\n readonly local: string;\n readonly namespace: boolean;\n readonly specifier: string;\n readonly tag?: Tag;\n}\n\nexport interface Imports {\n readonly imports: readonly HtmlImport[];\n /** `export { html } from 'specifier'` (its node, for a code frame). */\n readonly reexports: readonly { readonly node: Node; readonly specifier: string }[];\n}\n\nexport interface Uses {\n /** TaggedTemplateExpression nodes whose tag is a matching binding, and which tag it is. */\n readonly sites: readonly {\n readonly node: Node;\n readonly binding: HtmlImport;\n readonly tag: Tag;\n }[];\n /** Other references; `exported`: an `export { html }` specifier. */\n readonly leftovers: readonly { readonly node: Node; readonly exported: boolean }[];\n}\n\nconst nameOf = (node: Node | undefined): string | undefined =>\n node === undefined\n ? undefined\n : node.type === 'Identifier'\n ? (node['name'] as string)\n : node.type === 'Literal' && typeof node['value'] === 'string'\n ? node['value']\n : undefined;\n\nconst specifierOf = (node: Node): string | undefined => nameOf(nodeAt(node, 'source'));\n\nconst isValue = (node: Node): boolean =>\n node['importKind'] !== 'type' && node['exportKind'] !== 'type';\n\n/**\n * Imports of `html` and `svg` (named) and namespace imports, plus `export { html } from`\n * re-exports (of either tag).\n */\nexport function htmlImports(program: Node): Imports {\n const imports: HtmlImport[] = [];\n const reexports: { node: Node; specifier: string }[] = [];\n for (const statement of nodesAt(program, 'body')) {\n const specifier = specifierOf(statement);\n if (specifier === undefined || !isValue(statement)) continue;\n if (statement.type === 'ImportDeclaration') {\n for (const s of nodesAt(statement, 'specifiers')) {\n const local = nameOf(nodeAt(s, 'local'));\n if (local === undefined || !isValue(s)) continue;\n if (s.type === 'ImportNamespaceSpecifier') {\n imports.push({ local, namespace: true, specifier });\n } else if (s.type === 'ImportSpecifier') {\n const tag = tagOf(nameOf(nodeAt(s, 'imported')));\n if (tag !== undefined) imports.push({ local, namespace: false, specifier, tag });\n }\n }\n } else if (statement.type === 'ExportNamedDeclaration') {\n const html = nodesAt(statement, 'specifiers').find(\n (s) => isValue(s) && tagOf(nameOf(nodeAt(s, 'local'))) !== undefined,\n );\n if (html !== undefined) reexports.push({ node: html, specifier });\n }\n }\n return { imports, reexports };\n}\n\nconst unparen = (node: Node | undefined): Node | undefined =>\n node?.type === 'ParenthesizedExpression' ? unparen(nodeAt(node, 'expression')) : node;\n\n/** Every use of `bindings` in `program`, skipping names that a nested scope redeclares. */\nexport function findUses(program: Node, bindings: readonly HtmlImport[]): Uses {\n const byName = new Map(bindings.map((b) => [b.local, b]));\n const sites: { node: Node; binding: HtmlImport; tag: Tag }[] = [];\n const leftovers: { node: Node; exported: boolean }[] = [];\n const scopes: Set<string>[] = [];\n\n const binding = (node: Node | undefined): HtmlImport | undefined => {\n const name = node?.type === 'Identifier' ? (node['name'] as string) : undefined;\n if (name === undefined || scopes.some((s) => s.has(name))) return undefined;\n return byName.get(name);\n };\n\n /** The binding a tag refers to, and which tag: `html` itself, or `ns.html` (or svg). */\n const tagBinding = (tag: Node | undefined): { binding: HtmlImport; tag: Tag } | undefined => {\n if (tag?.type === 'Identifier') {\n const b = binding(tag);\n return b?.tag === undefined ? undefined : { binding: b, tag: b.tag };\n }\n if (tag?.type !== 'MemberExpression' || tag['computed'] === true) return undefined;\n const b = binding(nodeAt(tag, 'object'));\n const name = tagOf(nameOf(nodeAt(tag, 'property')));\n return b?.namespace === true && name !== undefined ? { binding: b, tag: name } : undefined;\n };\n\n const walkAll = (nodes: readonly Node[]): void => {\n for (const n of nodes) walk(n);\n };\n\n function visit(node: Node): void {\n switch (node.type) {\n case 'ImportDeclaration':\n case 'ExportAllDeclaration':\n case 'MetaProperty':\n case 'BreakStatement':\n case 'ContinueStatement':\n return;\n case 'ExportNamedDeclaration':\n if (nodeAt(node, 'source') === undefined) {\n for (const s of nodesAt(node, 'specifiers')) {\n if (binding(nodeAt(s, 'local')) !== undefined)\n leftovers.push({ node: s, exported: true });\n }\n }\n walkAll(nodesAt(node, 'declaration'));\n return;\n case 'TaggedTemplateExpression': {\n const tag = unparen(nodeAt(node, 'tag'));\n const b = tagBinding(tag);\n if (b === undefined) break;\n sites.push({ node, ...b });\n walkAll(nodesAt(nodeAt(node, 'quasi') ?? node, 'expressions'));\n return;\n }\n case 'MemberExpression': {\n const object = nodeAt(node, 'object');\n const b = binding(object);\n const computed = node['computed'] === true;\n if (b !== undefined) {\n // ns.other is fine; ns.html (or ns.svg) outside a tag, or ns[expr], can't be followed.\n if (!b.namespace || computed || tagOf(nameOf(nodeAt(node, 'property'))) !== undefined) {\n leftovers.push({ node, exported: false });\n }\n } else walkAll(nodesAt(node, 'object'));\n if (computed) walkAll(nodesAt(node, 'property'));\n return;\n }\n case 'Property':\n case 'MethodDefinition':\n case 'PropertyDefinition':\n case 'AccessorProperty':\n if (node['computed'] === true) walkAll(nodesAt(node, 'key'));\n walkAll(nodesAt(node, 'decorators'));\n walkAll(nodesAt(node, 'value'));\n return;\n case 'LabeledStatement':\n walkAll(nodesAt(node, 'body'));\n return;\n case 'Identifier':\n if (binding(node) !== undefined) leftovers.push({ node, exported: false });\n return;\n }\n walkAll(children(node));\n }\n\n function walk(node: Node): void {\n const declared = scopeNames(node)?.filter((name) => byName.has(name));\n if (declared === undefined || declared.length === 0) {\n visit(node);\n return;\n }\n scopes.push(new Set(declared));\n visit(node);\n scopes.pop();\n }\n\n if (byName.size > 0) walkAll(nodesAt(program, 'body'));\n return { sites, leftovers };\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface Scope {
|
|
2
|
+
/** Whether module `id` can affect Gyral components, so the scan must read it. */
|
|
3
|
+
inScope(id: string): boolean;
|
|
4
|
+
/** The name of the package module `id` belongs to (`@gyral/core` is where `raw` comes from). */
|
|
5
|
+
packageOf(id: string): string | undefined;
|
|
6
|
+
}
|
|
7
|
+
/** A scope for one build: manifests and answers are cached per package directory. */
|
|
8
|
+
export declare function createScope(): Scope;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Which modules the build-time feature scan reads (features.ts, view/05-element.md "Features
|
|
2
|
+
// register themselves"): only those that can affect Gyral components. A module outside
|
|
3
|
+
// node_modules (the app's own source, a workspace or `link:` package) always can. An installed
|
|
4
|
+
// package can only if it is a Gyral package or depends on one, directly or through its own
|
|
5
|
+
// dependencies (a design system, a component library): a package that never reaches
|
|
6
|
+
// `@gyral/*` can't import `define`, `html` or `raw`, so it can't define a component, render a
|
|
7
|
+
// template or set a spec field (effect, three and the like). The manifests are read from disk
|
|
8
|
+
// the way Node finds packages (no exports map), once per build.
|
|
9
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
/** Gyral's packages (vite.ts has the same pattern for the dev server's dependents). */
|
|
12
|
+
const GYRAL_PACKAGES = /^@gyral\//;
|
|
13
|
+
/** The last `node_modules` directory in a path, and what follows it. */
|
|
14
|
+
const IN_NODE_MODULES = /^(.*[\\/]node_modules[\\/])((?:@[^\\/]+[\\/])?[^\\/]+)[\\/]/;
|
|
15
|
+
function read(file) {
|
|
16
|
+
try {
|
|
17
|
+
const value = JSON.parse(readFileSync(file, 'utf8'));
|
|
18
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** The installed package `name` as seen from `dir` (Node's lookup), as a real path. */
|
|
25
|
+
function locate(dir, name) {
|
|
26
|
+
for (let d = dir;; d = dirname(d)) {
|
|
27
|
+
const candidate = join(d, 'node_modules', name);
|
|
28
|
+
if (read(join(candidate, 'package.json')) !== undefined) {
|
|
29
|
+
try {
|
|
30
|
+
return realpathSync(candidate);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return candidate;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (dirname(d) === d)
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const dependencyNames = (m) => Object.keys({ ...m.dependencies, ...m.peerDependencies, ...m.optionalDependencies });
|
|
41
|
+
/** A scope for one build: manifests and answers are cached per package directory. */
|
|
42
|
+
export function createScope() {
|
|
43
|
+
const reaches = new Map();
|
|
44
|
+
const names = new Map();
|
|
45
|
+
/** Whether the package in `dir`, or any package it depends on, is a Gyral package. */
|
|
46
|
+
function reachesGyral(dir) {
|
|
47
|
+
const known = reaches.get(dir);
|
|
48
|
+
if (known !== undefined)
|
|
49
|
+
return known;
|
|
50
|
+
// A walk of the dependency graph from `dir` (cycles are fine: each package is read once).
|
|
51
|
+
const seen = new Set([dir]);
|
|
52
|
+
const queue = [dir];
|
|
53
|
+
let found = false;
|
|
54
|
+
for (let next = queue.pop(); next !== undefined && !found; next = queue.pop()) {
|
|
55
|
+
const m = read(join(next, 'package.json'));
|
|
56
|
+
if (m === undefined) {
|
|
57
|
+
found = next === dir; // the module's own package can't be read: scan it
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const deps = dependencyNames(m);
|
|
61
|
+
if ((typeof m.name === 'string' && GYRAL_PACKAGES.test(m.name)) ||
|
|
62
|
+
deps.some((d) => GYRAL_PACKAGES.test(d))) {
|
|
63
|
+
found = true;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
for (const d of deps) {
|
|
67
|
+
const at = locate(next, d);
|
|
68
|
+
if (at === undefined || seen.has(at))
|
|
69
|
+
continue;
|
|
70
|
+
if (reaches.get(at) === true) {
|
|
71
|
+
found = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
seen.add(at);
|
|
75
|
+
queue.push(at);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Not found: the whole graph from `dir` was read, and no package in it reaches Gyral.
|
|
79
|
+
if (!found)
|
|
80
|
+
for (const s of seen)
|
|
81
|
+
reaches.set(s, false);
|
|
82
|
+
reaches.set(dir, found);
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
/** The `name` of the nearest package.json above `file` that has one. */
|
|
86
|
+
function nameAbove(file) {
|
|
87
|
+
const dir = dirname(file);
|
|
88
|
+
if (names.has(dir))
|
|
89
|
+
return names.get(dir);
|
|
90
|
+
const name = read(join(dir, 'package.json'))?.name;
|
|
91
|
+
const result = typeof name === 'string' ? name : dirname(dir) === dir ? undefined : nameAbove(dir);
|
|
92
|
+
names.set(dir, result);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
inScope(id) {
|
|
97
|
+
const installed = IN_NODE_MODULES.exec(id);
|
|
98
|
+
if (installed === null)
|
|
99
|
+
return true;
|
|
100
|
+
const dir = (installed[1] ?? '') + (installed[2] ?? '');
|
|
101
|
+
let real = dir;
|
|
102
|
+
try {
|
|
103
|
+
real = realpathSync(dir);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// keep the path as given
|
|
107
|
+
}
|
|
108
|
+
return reachesGyral(real);
|
|
109
|
+
},
|
|
110
|
+
packageOf(id) {
|
|
111
|
+
const installed = IN_NODE_MODULES.exec(id);
|
|
112
|
+
return installed === null ? nameAbove(id) : installed[2]?.replace(/\\/g, '/');
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/compiler/scope.ts"],"names":[],"mappings":"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","sourcesContent":["// Which modules the build-time feature scan reads (features.ts, view/05-element.md \"Features\n// register themselves\"): only those that can affect Gyral components. A module outside\n// node_modules (the app's own source, a workspace or `link:` package) always can. An installed\n// package can only if it is a Gyral package or depends on one, directly or through its own\n// dependencies (a design system, a component library): a package that never reaches\n// `@gyral/*` can't import `define`, `html` or `raw`, so it can't define a component, render a\n// template or set a spec field (effect, three and the like). The manifests are read from disk\n// the way Node finds packages (no exports map), once per build.\nimport { readFileSync, realpathSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\n/** Gyral's packages (vite.ts has the same pattern for the dev server's dependents). */\nconst GYRAL_PACKAGES = /^@gyral\\//;\n/** The last `node_modules` directory in a path, and what follows it. */\nconst IN_NODE_MODULES = /^(.*[\\\\/]node_modules[\\\\/])((?:@[^\\\\/]+[\\\\/])?[^\\\\/]+)[\\\\/]/;\n\ninterface Manifest {\n readonly name?: unknown;\n readonly dependencies?: object;\n readonly peerDependencies?: object;\n readonly optionalDependencies?: object;\n}\n\nfunction read(file: string): Manifest | undefined {\n try {\n const value: unknown = JSON.parse(readFileSync(file, 'utf8'));\n return typeof value === 'object' && value !== null ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The installed package `name` as seen from `dir` (Node's lookup), as a real path. */\nfunction locate(dir: string, name: string): string | undefined {\n for (let d = dir; ; d = dirname(d)) {\n const candidate = join(d, 'node_modules', name);\n if (read(join(candidate, 'package.json')) !== undefined) {\n try {\n return realpathSync(candidate);\n } catch {\n return candidate;\n }\n }\n if (dirname(d) === d) return undefined;\n }\n}\n\nconst dependencyNames = (m: Manifest): string[] =>\n Object.keys({ ...m.dependencies, ...m.peerDependencies, ...m.optionalDependencies });\n\nexport interface Scope {\n /** Whether module `id` can affect Gyral components, so the scan must read it. */\n inScope(id: string): boolean;\n /** The name of the package module `id` belongs to (`@gyral/core` is where `raw` comes from). */\n packageOf(id: string): string | undefined;\n}\n\n/** A scope for one build: manifests and answers are cached per package directory. */\nexport function createScope(): Scope {\n const reaches = new Map<string, boolean>();\n const names = new Map<string, string | undefined>();\n\n /** Whether the package in `dir`, or any package it depends on, is a Gyral package. */\n function reachesGyral(dir: string): boolean {\n const known = reaches.get(dir);\n if (known !== undefined) return known;\n // A walk of the dependency graph from `dir` (cycles are fine: each package is read once).\n const seen = new Set([dir]);\n const queue = [dir];\n let found = false;\n for (let next = queue.pop(); next !== undefined && !found; next = queue.pop()) {\n const m = read(join(next, 'package.json'));\n if (m === undefined) {\n found = next === dir; // the module's own package can't be read: scan it\n continue;\n }\n const deps = dependencyNames(m);\n if (\n (typeof m.name === 'string' && GYRAL_PACKAGES.test(m.name)) ||\n deps.some((d) => GYRAL_PACKAGES.test(d))\n ) {\n found = true;\n break;\n }\n for (const d of deps) {\n const at = locate(next, d);\n if (at === undefined || seen.has(at)) continue;\n if (reaches.get(at) === true) {\n found = true;\n break;\n }\n seen.add(at);\n queue.push(at);\n }\n }\n // Not found: the whole graph from `dir` was read, and no package in it reaches Gyral.\n if (!found) for (const s of seen) reaches.set(s, false);\n reaches.set(dir, found);\n return found;\n }\n\n /** The `name` of the nearest package.json above `file` that has one. */\n function nameAbove(file: string): string | undefined {\n const dir = dirname(file);\n if (names.has(dir)) return names.get(dir);\n const name = read(join(dir, 'package.json'))?.name;\n const result =\n typeof name === 'string' ? name : dirname(dir) === dir ? undefined : nameAbove(dir);\n names.set(dir, result);\n return result;\n }\n\n return {\n inScope(id) {\n const installed = IN_NODE_MODULES.exec(id);\n if (installed === null) return true;\n const dir = (installed[1] ?? '') + (installed[2] ?? '');\n let real = dir;\n try {\n real = realpathSync(dir);\n } catch {\n // keep the path as given\n }\n return reachesGyral(real);\n },\n packageOf(id) {\n const installed = IN_NODE_MODULES.exec(id);\n return installed === null ? nameAbove(id) : installed[2]?.replace(/\\\\/g, '/');\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { ElementTypesOptions } from '../vite.js';
|
|
3
|
+
export interface TagMapOptions {
|
|
4
|
+
/** Project root; `dirs` and `out` are relative to it. */
|
|
5
|
+
readonly root: string;
|
|
6
|
+
/** Source directories to scan, recursively. Default `['src']`. */
|
|
7
|
+
readonly dirs?: readonly string[];
|
|
8
|
+
/** The declaration file to write. Default `.gyral/elements.d.ts`. */
|
|
9
|
+
readonly out?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface TagEntry {
|
|
12
|
+
readonly tag: string;
|
|
13
|
+
readonly file: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
}
|
|
16
|
+
/** One `define()`d component: its tag, the file defining it and, if exported, its binding. */
|
|
17
|
+
export interface Discovered {
|
|
18
|
+
readonly tag: string;
|
|
19
|
+
/** Absolute path of the defining module. */
|
|
20
|
+
readonly file: string;
|
|
21
|
+
/** `X` in `export const X = define…`; undefined for a component that isn't exported. */
|
|
22
|
+
readonly name: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
/** The components defined in one module's source text. */
|
|
25
|
+
export declare function componentsIn(code: string, file: string): Discovered[];
|
|
26
|
+
/** Every component under `dirs` (relative to `root`), in file then source order. */
|
|
27
|
+
export declare function discoverComponents(root: string, dirs: readonly string[]): Discovered[];
|
|
28
|
+
/** Every exported component under `dirs` (default `['src']`), sorted by tag. */
|
|
29
|
+
export declare function scanTags(options: TagMapOptions): TagEntry[];
|
|
30
|
+
/** The declaration file's text for `entries`, written to `outFile`. */
|
|
31
|
+
export declare function tagMapSource(entries: readonly TagEntry[], outFile: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Scans the sources and writes the declaration file when its content changed. Returns the
|
|
34
|
+
* entries and whether the file was written.
|
|
35
|
+
*/
|
|
36
|
+
export declare function writeTagMap(options: TagMapOptions): {
|
|
37
|
+
entries: TagEntry[];
|
|
38
|
+
written: boolean;
|
|
39
|
+
};
|
|
40
|
+
/** The preset's `gyral:element-types` plugin (`gyralVitePreset({ types })`). */
|
|
41
|
+
export declare function elementTypesPlugin(options: ElementTypesOptions): Plugin;
|