@lightspeed-js/core 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile.js +4 -2
- package/dist/ssg.js +48 -16
- package/dist/types/src/parser/template.d.ts +6 -0
- package/dist/types/src/project/component-ids.d.ts +9 -1
- package/dist/types/src/project/components.d.ts +16 -2
- package/dist/types/src/ssg/fragment.d.ts +3 -3
- package/dist/types/src/vite/plugin.d.ts +7 -1
- package/dist/types/test/component-prefix.test.d.ts +1 -0
- package/dist/types/test/eager.test.d.ts +1 -0
- package/dist/vite.js +83 -30
- package/package.json +1 -1
package/dist/compile.js
CHANGED
|
@@ -188,12 +188,14 @@ function emitMountPlaceholders(template, componentRegistry, depth = 0) {
|
|
|
188
188
|
let result = template;
|
|
189
189
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*\/>/g, (_match, name, attributes) => {
|
|
190
190
|
warnUnknown(name);
|
|
191
|
-
|
|
191
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
192
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", "", entry?.needs);
|
|
192
193
|
});
|
|
193
194
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*>([\s\S]*?)<\/\1\s*>/g, (_match, name, attributes, content) => {
|
|
194
195
|
warnUnknown(name);
|
|
195
196
|
const inner = emitMountPlaceholders(content, componentRegistry, depth + 1);
|
|
196
|
-
|
|
197
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
198
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", inner, entry?.needs);
|
|
197
199
|
});
|
|
198
200
|
return result;
|
|
199
201
|
}
|
package/dist/ssg.js
CHANGED
|
@@ -3601,22 +3601,24 @@ function addMarkerBinds(payload, params, add) {
|
|
|
3601
3601
|
}
|
|
3602
3602
|
|
|
3603
3603
|
// src/project/component-ids.ts
|
|
3604
|
-
function componentIdOf(file) {
|
|
3604
|
+
function componentIdOf(file, prefix = "") {
|
|
3605
3605
|
const name = file.split(/[\\/]/).pop() ?? file;
|
|
3606
|
-
return name.replace(/\.lsp$/, "").toLowerCase();
|
|
3606
|
+
return (prefix + name.replace(/\.lsp$/, "")).toLowerCase();
|
|
3607
3607
|
}
|
|
3608
3608
|
function resolveComponentIds(found, declaredOverrides = []) {
|
|
3609
3609
|
const declared = new Set(declaredOverrides.map((id) => id.toLowerCase()));
|
|
3610
3610
|
const files = new Map;
|
|
3611
3611
|
const rootOf = new Map;
|
|
3612
|
-
const
|
|
3612
|
+
const roots = new Map;
|
|
3613
|
+
const result = { files, overrides: [], conflicts: [], unusedOverrides: [], roots };
|
|
3613
3614
|
const used = new Set;
|
|
3614
|
-
for (const { file, root } of found) {
|
|
3615
|
-
const id = componentIdOf(file);
|
|
3615
|
+
for (const { file, root, prefix = "" } of found) {
|
|
3616
|
+
const id = componentIdOf(file, prefix);
|
|
3616
3617
|
const kept = files.get(id);
|
|
3617
3618
|
if (kept === undefined) {
|
|
3618
3619
|
files.set(id, file);
|
|
3619
3620
|
rootOf.set(id, root);
|
|
3621
|
+
roots.set(id, { root, prefix });
|
|
3620
3622
|
continue;
|
|
3621
3623
|
}
|
|
3622
3624
|
if (rootOf.get(id) === root) {
|
|
@@ -3643,7 +3645,7 @@ function describeConflict(conflict, rel) {
|
|
|
3643
3645
|
// src/project/components.ts
|
|
3644
3646
|
import { readFile, readdir } from "fs/promises";
|
|
3645
3647
|
import { createRequire as createRequire2 } from "node:module";
|
|
3646
|
-
import { dirname as dirname2, join, relative as relative2, resolve as resolve2 } from "path";
|
|
3648
|
+
import { dirname as dirname2, join, relative as relative2, resolve as resolve2, sep } from "path";
|
|
3647
3649
|
|
|
3648
3650
|
// src/specifiers.ts
|
|
3649
3651
|
var PACKAGE_NAME = "@lightspeed-js/core";
|
|
@@ -4292,12 +4294,14 @@ function emitMountPlaceholders(template, componentRegistry, depth = 0) {
|
|
|
4292
4294
|
let result = template;
|
|
4293
4295
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*\/>/g, (_match, name, attributes) => {
|
|
4294
4296
|
warnUnknown(name);
|
|
4295
|
-
|
|
4297
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
4298
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", "", entry?.needs);
|
|
4296
4299
|
});
|
|
4297
4300
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*>([\s\S]*?)<\/\1\s*>/g, (_match, name, attributes, content) => {
|
|
4298
4301
|
warnUnknown(name);
|
|
4299
4302
|
const inner = emitMountPlaceholders(content, componentRegistry, depth + 1);
|
|
4300
|
-
|
|
4303
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
4304
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", inner, entry?.needs);
|
|
4301
4305
|
});
|
|
4302
4306
|
return result;
|
|
4303
4307
|
}
|
|
@@ -4406,18 +4410,43 @@ function createComponentProject(options) {
|
|
|
4406
4410
|
const announced = new Set;
|
|
4407
4411
|
let rebuilding = null;
|
|
4408
4412
|
const projectModules = new Map;
|
|
4409
|
-
function
|
|
4413
|
+
function rootSpecs() {
|
|
4410
4414
|
const root = rootOf();
|
|
4411
|
-
return [
|
|
4415
|
+
return [
|
|
4416
|
+
{ dir: resolve2(root, "src"), prefix: "" },
|
|
4417
|
+
...extraComponentDirs.map((entry) => typeof entry === "string" ? { dir: resolve2(root, entry), prefix: "" } : { dir: resolve2(root, entry.dir), prefix: entry.prefix ?? "" })
|
|
4418
|
+
];
|
|
4419
|
+
}
|
|
4420
|
+
function roots() {
|
|
4421
|
+
return rootSpecs().map((spec) => spec.dir);
|
|
4412
4422
|
}
|
|
4413
4423
|
async function findComponentFiles() {
|
|
4424
|
+
const specs = rootSpecs();
|
|
4414
4425
|
const found = [];
|
|
4415
|
-
for (const
|
|
4416
|
-
|
|
4417
|
-
|
|
4426
|
+
for (const spec of specs) {
|
|
4427
|
+
const nested = new Set(specs.map((s) => s.dir).filter((d) => d !== spec.dir && d.startsWith(spec.dir + sep)));
|
|
4428
|
+
for (const file of await findLspFiles(spec.dir, nested)) {
|
|
4429
|
+
found.push({ file, root: spec.dir, ...spec.prefix ? { prefix: spec.prefix } : {} });
|
|
4430
|
+
}
|
|
4418
4431
|
}
|
|
4419
4432
|
return found;
|
|
4420
4433
|
}
|
|
4434
|
+
let rootOfId = new Map;
|
|
4435
|
+
function registryFor(id) {
|
|
4436
|
+
const all = Object.fromEntries(registry);
|
|
4437
|
+
const home = rootOfId.get(id);
|
|
4438
|
+
if (!home?.prefix)
|
|
4439
|
+
return all;
|
|
4440
|
+
for (const [sibling, where] of rootOfId) {
|
|
4441
|
+
if (where.root !== home.root)
|
|
4442
|
+
continue;
|
|
4443
|
+
const local = sibling.slice(home.prefix.length);
|
|
4444
|
+
const entry = registry.get(sibling);
|
|
4445
|
+
if (local && entry)
|
|
4446
|
+
all[local] = { ...entry, id: sibling };
|
|
4447
|
+
}
|
|
4448
|
+
return all;
|
|
4449
|
+
}
|
|
4421
4450
|
function announce(ids) {
|
|
4422
4451
|
const rel = (file) => relative2(rootOf(), file);
|
|
4423
4452
|
for (const conflict of ids.conflicts) {
|
|
@@ -4443,6 +4472,7 @@ function createComponentProject(options) {
|
|
|
4443
4472
|
const nextFiles = new Map;
|
|
4444
4473
|
try {
|
|
4445
4474
|
const ids = resolveComponentIds(await findComponentFiles(), declaredOverrides);
|
|
4475
|
+
rootOfId = ids.roots;
|
|
4446
4476
|
conflicts = ids.conflicts;
|
|
4447
4477
|
announce(ids);
|
|
4448
4478
|
for (const [componentId, filePath] of ids.files) {
|
|
@@ -4507,9 +4537,9 @@ function createComponentProject(options) {
|
|
|
4507
4537
|
}
|
|
4508
4538
|
async function definitions() {
|
|
4509
4539
|
await rebuild();
|
|
4510
|
-
const registryObj = Object.fromEntries(registry);
|
|
4511
4540
|
const out = [];
|
|
4512
4541
|
for (const [componentId, raw] of registry) {
|
|
4542
|
+
const registryObj = registryFor(componentId);
|
|
4513
4543
|
const scopeId = scopeIdFor(componentId);
|
|
4514
4544
|
const source = raw.logic?.trim() ? await compileLogic2(raw.logic) : "";
|
|
4515
4545
|
const setup = await buildSetup(source, {
|
|
@@ -4545,6 +4575,7 @@ function createComponentProject(options) {
|
|
|
4545
4575
|
return conflicts;
|
|
4546
4576
|
},
|
|
4547
4577
|
roots,
|
|
4578
|
+
registryFor,
|
|
4548
4579
|
rebuild,
|
|
4549
4580
|
definitions,
|
|
4550
4581
|
compileMarkup: (html, id = "index") => transformTemplate(html, id, Object.fromEntries(registry)),
|
|
@@ -4560,14 +4591,15 @@ function withSharedStyles(parsed, watch) {
|
|
|
4560
4591
|
watch(file);
|
|
4561
4592
|
return inlineStyleSources(parsed, nodeStyleReader());
|
|
4562
4593
|
}
|
|
4563
|
-
async function findLspFiles(dir) {
|
|
4594
|
+
async function findLspFiles(dir, skip = new Set) {
|
|
4564
4595
|
const found = [];
|
|
4565
4596
|
try {
|
|
4566
4597
|
const entries = (await readdir(dir, { withFileTypes: true })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
4567
4598
|
for (const entry of entries) {
|
|
4568
4599
|
const fullPath = join(dir, entry.name);
|
|
4569
4600
|
if (entry.isDirectory()) {
|
|
4570
|
-
|
|
4601
|
+
if (!skip.has(fullPath))
|
|
4602
|
+
found.push(...await findLspFiles(fullPath, skip));
|
|
4571
4603
|
} else if (entry.isFile() && entry.name.endsWith(".lsp")) {
|
|
4572
4604
|
found.push(fullPath);
|
|
4573
4605
|
}
|
|
@@ -5,6 +5,12 @@ interface ComponentRegistry {
|
|
|
5
5
|
logic: string;
|
|
6
6
|
/** Cell keys the component itself declared - see `LSPComponent.needs`. */
|
|
7
7
|
needs?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* The id the component registers under, when the tag is a local name for
|
|
10
|
+
* it: inside a prefixed component root, a sibling's unprefixed name
|
|
11
|
+
* (ADR-061). Absent, the tag lowercased is the id.
|
|
12
|
+
*/
|
|
13
|
+
id?: string;
|
|
8
14
|
};
|
|
9
15
|
}
|
|
10
16
|
/**
|
|
@@ -15,6 +15,8 @@ export interface FoundComponent {
|
|
|
15
15
|
file: string;
|
|
16
16
|
/** The component root it was found under. */
|
|
17
17
|
root: string;
|
|
18
|
+
/** The root's prefix (ADR-061): its components register as prefix + name. */
|
|
19
|
+
prefix?: string;
|
|
18
20
|
}
|
|
19
21
|
export type Conflict = {
|
|
20
22
|
kind: "same-root";
|
|
@@ -39,8 +41,14 @@ export interface ComponentIds {
|
|
|
39
41
|
conflicts: Conflict[];
|
|
40
42
|
/** Names in `overrides` that replace nothing - left over after a rename or removal. */
|
|
41
43
|
unusedOverrides: string[];
|
|
44
|
+
/** Component id to the root it came from, and that root's prefix. */
|
|
45
|
+
roots: Map<string, {
|
|
46
|
+
root: string;
|
|
47
|
+
prefix: string;
|
|
48
|
+
}>;
|
|
42
49
|
}
|
|
43
|
-
|
|
50
|
+
/** A component's id: its root's prefix and its filename, lowercased (`Slv` + `Icon.lsp` → `slvicon`). */
|
|
51
|
+
export declare function componentIdOf(file: string, prefix?: string): string;
|
|
44
52
|
/** `found` must be in precedence order: every file of the first root, then the next. */
|
|
45
53
|
export declare function resolveComponentIds(found: FoundComponent[], declaredOverrides?: readonly string[]): ComponentIds;
|
|
46
54
|
/** One message per conflict, each ending in the change that fixes it (ADR-055). */
|
|
@@ -11,11 +11,21 @@ import { parseLSP } from "../parser/index.js";
|
|
|
11
11
|
import type { ComponentDefinition } from "../runtime/components/index.js";
|
|
12
12
|
import { type Conflict } from "./component-ids.js";
|
|
13
13
|
export type Compiler = "esbuild" | "tsc" | "swc";
|
|
14
|
+
/** A component root: a folder, or a folder with a prefix (ADR-061). */
|
|
15
|
+
export type ComponentDir = string | {
|
|
16
|
+
dir: string;
|
|
17
|
+
prefix?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare const componentDirPath: (entry: ComponentDir) => string;
|
|
14
20
|
export interface ComponentProjectOptions {
|
|
15
21
|
/** The project root. A function when it is only known later (Vite's configResolved). */
|
|
16
22
|
root: string | (() => string);
|
|
17
|
-
/**
|
|
18
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Roots beyond the project's own `src`, relative to the root. An entry with
|
|
25
|
+
* a `prefix` registers its components as prefix + name (ADR-061), and may
|
|
26
|
+
* be a folder inside `src`, which then belongs to it rather than to `src`.
|
|
27
|
+
*/
|
|
28
|
+
componentDirs?: ComponentDir[];
|
|
19
29
|
/** Names the app replaces on purpose (ADR-058). */
|
|
20
30
|
overrides?: string[];
|
|
21
31
|
/** For component logic at build time. */
|
|
@@ -39,6 +49,10 @@ export interface ComponentProject {
|
|
|
39
49
|
readonly conflicts: Conflict[];
|
|
40
50
|
/** Every root components come from, in precedence order: `src` first. */
|
|
41
51
|
roots(): string[];
|
|
52
|
+
/** The registry as `id`'s template sees it: with its root's siblings under their local names (ADR-061). */
|
|
53
|
+
registryFor(id: string): Record<string, RegistryEntry & {
|
|
54
|
+
id?: string;
|
|
55
|
+
}>;
|
|
42
56
|
/** Rescan the roots. Concurrent callers share one rescan. */
|
|
43
57
|
rebuild(): Promise<void>;
|
|
44
58
|
/** Compile every component, as the client's modules do, for build-time rendering. */
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* `app.hydrate(container)` on the element it put the fragment in.
|
|
14
14
|
*/
|
|
15
15
|
import type { TemplateExpression } from "../compile/expressions.js";
|
|
16
|
-
import type { ComponentProject, Compiler } from "../project/components.js";
|
|
16
|
+
import type { ComponentDir, ComponentProject, Compiler } from "../project/components.js";
|
|
17
17
|
export interface FragmentRendererOptions {
|
|
18
18
|
/** The project root: components are found in its `src`, then `componentDirs`. */
|
|
19
19
|
root: string;
|
|
20
|
-
/** Further component roots, relative to the root - as `lightspeedPlugin`'s. */
|
|
21
|
-
componentDirs?:
|
|
20
|
+
/** Further component roots, relative to the root - as `lightspeedPlugin`'s, prefixes included. */
|
|
21
|
+
componentDirs?: ComponentDir[];
|
|
22
22
|
/** Names the app replaces on purpose (ADR-058). */
|
|
23
23
|
overrides?: string[];
|
|
24
24
|
compiler?: Compiler;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
import { type ComponentDir } from "../project/components.js";
|
|
2
3
|
interface LightspeedPluginOptions {
|
|
3
4
|
typescript?: boolean;
|
|
4
5
|
/**
|
|
@@ -62,8 +63,13 @@ interface LightspeedPluginOptions {
|
|
|
62
63
|
* wherever it was found. The project's own `src` is searched first and wins,
|
|
63
64
|
* so an app can replace a package's component by giving a file the same
|
|
64
65
|
* name - once it says so in `overrides` (ADR-058).
|
|
66
|
+
*
|
|
67
|
+
* `{ dir, prefix }` namespaces a root (ADR-061): its components register as
|
|
68
|
+
* prefix + name (`{ dir: 'node_modules/ui', prefix: 'Ui' }` makes its
|
|
69
|
+
* `Button.lsp` `<UiButton>`), and inside it a sibling's plain name still
|
|
70
|
+
* finds the sibling. A prefixed folder inside `src` belongs to its entry.
|
|
65
71
|
*/
|
|
66
|
-
componentDirs?:
|
|
72
|
+
componentDirs?: ComponentDir[];
|
|
67
73
|
/**
|
|
68
74
|
* Component names the app replaces on purpose: a file in `src` (or an
|
|
69
75
|
* earlier root) with the same name as one in a later `componentDirs` root.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/vite.js
CHANGED
|
@@ -18,22 +18,24 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// src/project/component-ids.ts
|
|
21
|
-
function componentIdOf(file) {
|
|
21
|
+
function componentIdOf(file, prefix = "") {
|
|
22
22
|
const name = file.split(/[\\/]/).pop() ?? file;
|
|
23
|
-
return name.replace(/\.lsp$/, "").toLowerCase();
|
|
23
|
+
return (prefix + name.replace(/\.lsp$/, "")).toLowerCase();
|
|
24
24
|
}
|
|
25
25
|
function resolveComponentIds(found, declaredOverrides = []) {
|
|
26
26
|
const declared = new Set(declaredOverrides.map((id) => id.toLowerCase()));
|
|
27
27
|
const files = new Map;
|
|
28
28
|
const rootOf = new Map;
|
|
29
|
-
const
|
|
29
|
+
const roots = new Map;
|
|
30
|
+
const result = { files, overrides: [], conflicts: [], unusedOverrides: [], roots };
|
|
30
31
|
const used = new Set;
|
|
31
|
-
for (const { file, root } of found) {
|
|
32
|
-
const id = componentIdOf(file);
|
|
32
|
+
for (const { file, root, prefix = "" } of found) {
|
|
33
|
+
const id = componentIdOf(file, prefix);
|
|
33
34
|
const kept = files.get(id);
|
|
34
35
|
if (kept === undefined) {
|
|
35
36
|
files.set(id, file);
|
|
36
37
|
rootOf.set(id, root);
|
|
38
|
+
roots.set(id, { root, prefix });
|
|
37
39
|
continue;
|
|
38
40
|
}
|
|
39
41
|
if (rootOf.get(id) === root) {
|
|
@@ -60,7 +62,7 @@ function describeConflict(conflict, rel) {
|
|
|
60
62
|
// src/project/components.ts
|
|
61
63
|
import { readFile, readdir } from "fs/promises";
|
|
62
64
|
import { createRequire as createRequire2 } from "node:module";
|
|
63
|
-
import { dirname as dirname2, join, relative as relative2, resolve as resolve2 } from "path";
|
|
65
|
+
import { dirname as dirname2, join, relative as relative2, resolve as resolve2, sep } from "path";
|
|
64
66
|
|
|
65
67
|
// src/runtime/diagnostics.ts
|
|
66
68
|
var PREFIX = "[lightspeed]";
|
|
@@ -2556,12 +2558,14 @@ function emitMountPlaceholders(template, componentRegistry, depth = 0) {
|
|
|
2556
2558
|
let result = template;
|
|
2557
2559
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*\/>/g, (_match, name, attributes) => {
|
|
2558
2560
|
warnUnknown(name);
|
|
2559
|
-
|
|
2561
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
2562
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", "", entry?.needs);
|
|
2560
2563
|
});
|
|
2561
2564
|
result = result.replace(/<([A-Z][a-zA-Z0-9]*)((?:\s+[^>]*?)?)\s*>([\s\S]*?)<\/\1\s*>/g, (_match, name, attributes, content) => {
|
|
2562
2565
|
warnUnknown(name);
|
|
2563
2566
|
const inner = emitMountPlaceholders(content, componentRegistry, depth + 1);
|
|
2564
|
-
|
|
2567
|
+
const entry = componentRegistry?.[name.toLowerCase()];
|
|
2568
|
+
return buildPlaceholder(entry?.id ?? name, attributes || "", inner, entry?.needs);
|
|
2565
2569
|
});
|
|
2566
2570
|
return result;
|
|
2567
2571
|
}
|
|
@@ -2659,6 +2663,7 @@ function splitAttributes(componentId, attrString) {
|
|
|
2659
2663
|
}
|
|
2660
2664
|
|
|
2661
2665
|
// src/project/components.ts
|
|
2666
|
+
var componentDirPath = (entry) => typeof entry === "string" ? entry : entry.dir;
|
|
2662
2667
|
function createComponentProject(options) {
|
|
2663
2668
|
const rootOf = () => typeof options.root === "function" ? options.root() : options.root;
|
|
2664
2669
|
const extraComponentDirs = options.componentDirs ?? [];
|
|
@@ -2670,18 +2675,43 @@ function createComponentProject(options) {
|
|
|
2670
2675
|
const announced = new Set;
|
|
2671
2676
|
let rebuilding = null;
|
|
2672
2677
|
const projectModules = new Map;
|
|
2673
|
-
function
|
|
2678
|
+
function rootSpecs() {
|
|
2674
2679
|
const root = rootOf();
|
|
2675
|
-
return [
|
|
2680
|
+
return [
|
|
2681
|
+
{ dir: resolve2(root, "src"), prefix: "" },
|
|
2682
|
+
...extraComponentDirs.map((entry) => typeof entry === "string" ? { dir: resolve2(root, entry), prefix: "" } : { dir: resolve2(root, entry.dir), prefix: entry.prefix ?? "" })
|
|
2683
|
+
];
|
|
2684
|
+
}
|
|
2685
|
+
function roots() {
|
|
2686
|
+
return rootSpecs().map((spec) => spec.dir);
|
|
2676
2687
|
}
|
|
2677
2688
|
async function findComponentFiles() {
|
|
2689
|
+
const specs = rootSpecs();
|
|
2678
2690
|
const found = [];
|
|
2679
|
-
for (const
|
|
2680
|
-
|
|
2681
|
-
|
|
2691
|
+
for (const spec of specs) {
|
|
2692
|
+
const nested = new Set(specs.map((s) => s.dir).filter((d) => d !== spec.dir && d.startsWith(spec.dir + sep)));
|
|
2693
|
+
for (const file of await findLspFiles(spec.dir, nested)) {
|
|
2694
|
+
found.push({ file, root: spec.dir, ...spec.prefix ? { prefix: spec.prefix } : {} });
|
|
2695
|
+
}
|
|
2682
2696
|
}
|
|
2683
2697
|
return found;
|
|
2684
2698
|
}
|
|
2699
|
+
let rootOfId = new Map;
|
|
2700
|
+
function registryFor(id) {
|
|
2701
|
+
const all = Object.fromEntries(registry);
|
|
2702
|
+
const home = rootOfId.get(id);
|
|
2703
|
+
if (!home?.prefix)
|
|
2704
|
+
return all;
|
|
2705
|
+
for (const [sibling, where] of rootOfId) {
|
|
2706
|
+
if (where.root !== home.root)
|
|
2707
|
+
continue;
|
|
2708
|
+
const local = sibling.slice(home.prefix.length);
|
|
2709
|
+
const entry = registry.get(sibling);
|
|
2710
|
+
if (local && entry)
|
|
2711
|
+
all[local] = { ...entry, id: sibling };
|
|
2712
|
+
}
|
|
2713
|
+
return all;
|
|
2714
|
+
}
|
|
2685
2715
|
function announce(ids) {
|
|
2686
2716
|
const rel = (file) => relative2(rootOf(), file);
|
|
2687
2717
|
for (const conflict of ids.conflicts) {
|
|
@@ -2707,6 +2737,7 @@ function createComponentProject(options) {
|
|
|
2707
2737
|
const nextFiles = new Map;
|
|
2708
2738
|
try {
|
|
2709
2739
|
const ids = resolveComponentIds(await findComponentFiles(), declaredOverrides);
|
|
2740
|
+
rootOfId = ids.roots;
|
|
2710
2741
|
conflicts = ids.conflicts;
|
|
2711
2742
|
announce(ids);
|
|
2712
2743
|
for (const [componentId, filePath] of ids.files) {
|
|
@@ -2771,9 +2802,9 @@ function createComponentProject(options) {
|
|
|
2771
2802
|
}
|
|
2772
2803
|
async function definitions() {
|
|
2773
2804
|
await rebuild();
|
|
2774
|
-
const registryObj = Object.fromEntries(registry);
|
|
2775
2805
|
const out = [];
|
|
2776
2806
|
for (const [componentId, raw] of registry) {
|
|
2807
|
+
const registryObj = registryFor(componentId);
|
|
2777
2808
|
const scopeId = scopeIdFor(componentId);
|
|
2778
2809
|
const source = raw.logic?.trim() ? await compileLogic2(raw.logic) : "";
|
|
2779
2810
|
const setup = await buildSetup(source, {
|
|
@@ -2809,6 +2840,7 @@ function createComponentProject(options) {
|
|
|
2809
2840
|
return conflicts;
|
|
2810
2841
|
},
|
|
2811
2842
|
roots,
|
|
2843
|
+
registryFor,
|
|
2812
2844
|
rebuild,
|
|
2813
2845
|
definitions,
|
|
2814
2846
|
compileMarkup: (html, id = "index") => transformTemplate(html, id, Object.fromEntries(registry)),
|
|
@@ -2824,14 +2856,15 @@ function withSharedStyles(parsed, watch) {
|
|
|
2824
2856
|
watch(file);
|
|
2825
2857
|
return inlineStyleSources(parsed, nodeStyleReader());
|
|
2826
2858
|
}
|
|
2827
|
-
async function findLspFiles(dir) {
|
|
2859
|
+
async function findLspFiles(dir, skip = new Set) {
|
|
2828
2860
|
const found = [];
|
|
2829
2861
|
try {
|
|
2830
2862
|
const entries = (await readdir(dir, { withFileTypes: true })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
2831
2863
|
for (const entry of entries) {
|
|
2832
2864
|
const fullPath = join(dir, entry.name);
|
|
2833
2865
|
if (entry.isDirectory()) {
|
|
2834
|
-
|
|
2866
|
+
if (!skip.has(fullPath))
|
|
2867
|
+
found.push(...await findLspFiles(fullPath, skip));
|
|
2835
2868
|
} else if (entry.isFile() && entry.name.endsWith(".lsp")) {
|
|
2836
2869
|
found.push(fullPath);
|
|
2837
2870
|
}
|
|
@@ -4801,7 +4834,7 @@ function serialiseBetween(start, end) {
|
|
|
4801
4834
|
|
|
4802
4835
|
// src/vite/layout.ts
|
|
4803
4836
|
import { readFile as readFile2 } from "fs/promises";
|
|
4804
|
-
import { dirname as dirname3, join as join2, relative as relative3, resolve as resolve3, sep } from "path";
|
|
4837
|
+
import { dirname as dirname3, join as join2, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
|
|
4805
4838
|
|
|
4806
4839
|
// src/parser/layout.ts
|
|
4807
4840
|
var DECLARATION2 = /<!--\s*lsp:layout\s+([^\s>][^>]*?)\s*-->/;
|
|
@@ -4903,7 +4936,7 @@ async function findLayout(name, from, root) {
|
|
|
4903
4936
|
return { path: candidate, html: await readFile2(candidate, "utf-8") };
|
|
4904
4937
|
} catch {
|
|
4905
4938
|
}
|
|
4906
|
-
if (dir === stop || !dir.startsWith(stop +
|
|
4939
|
+
if (dir === stop || !dir.startsWith(stop + sep2))
|
|
4907
4940
|
return null;
|
|
4908
4941
|
const parent = dirname3(dir);
|
|
4909
4942
|
if (parent === dir)
|
|
@@ -5091,7 +5124,7 @@ function resolvedByHost(entry) {
|
|
|
5091
5124
|
// src/vite/plugin.ts
|
|
5092
5125
|
import { readFile as readFile3, readdir as readdir2, writeFile as writeFile2, mkdir as mkdir2, rm } from "fs/promises";
|
|
5093
5126
|
import { existsSync } from "fs";
|
|
5094
|
-
import { basename, dirname as dirname5, isAbsolute, join as join4, relative as relative4, resolve as resolve4, sep as
|
|
5127
|
+
import { basename, dirname as dirname5, isAbsolute, join as join4, relative as relative4, resolve as resolve4, sep as sep3 } from "path";
|
|
5095
5128
|
var VIRTUAL_COMPONENTS = "virtual:lightspeed/components";
|
|
5096
5129
|
var RESOLVED_COMPONENTS = "\x00" + VIRTUAL_COMPONENTS;
|
|
5097
5130
|
function lightspeedPlugin(options = {}) {
|
|
@@ -5214,7 +5247,9 @@ function lightspeedPlugin(options = {}) {
|
|
|
5214
5247
|
},
|
|
5215
5248
|
...extraComponentDirs.length > 0 ? {
|
|
5216
5249
|
server: {
|
|
5217
|
-
fs: {
|
|
5250
|
+
fs: {
|
|
5251
|
+
allow: [userRoot, ...extraComponentDirs.map((dir) => resolve4(userRoot, componentDirPath(dir)))]
|
|
5252
|
+
}
|
|
5218
5253
|
}
|
|
5219
5254
|
} : {}
|
|
5220
5255
|
};
|
|
@@ -5224,7 +5259,7 @@ function lightspeedPlugin(options = {}) {
|
|
|
5224
5259
|
const pages2 = await findPages(userRoot);
|
|
5225
5260
|
if (pages2.length === 0)
|
|
5226
5261
|
return fsAccess;
|
|
5227
|
-
const named = pages2.map((page) => relative4(userRoot, page).split(
|
|
5262
|
+
const named = pages2.map((page) => relative4(userRoot, page).split(sep3).join("/")).join(", ");
|
|
5228
5263
|
console.log(pages2.length === 1 ? `⚡ 1 page: ${named}` : `⚡ ${pages2.length} pages: ${named}`);
|
|
5229
5264
|
return {
|
|
5230
5265
|
...fsAccess,
|
|
@@ -5232,7 +5267,7 @@ function lightspeedPlugin(options = {}) {
|
|
|
5232
5267
|
build: {
|
|
5233
5268
|
rollupOptions: {
|
|
5234
5269
|
input: Object.fromEntries(pages2.map((page) => [
|
|
5235
|
-
relative4(userRoot, page).split(
|
|
5270
|
+
relative4(userRoot, page).split(sep3).join("/").replace(/\.html$/, ""),
|
|
5236
5271
|
page
|
|
5237
5272
|
]))
|
|
5238
5273
|
}
|
|
@@ -5293,12 +5328,12 @@ ${entries.join(`,
|
|
|
5293
5328
|
await project.rebuild();
|
|
5294
5329
|
const content = await readFile3(id, "utf-8");
|
|
5295
5330
|
const parsed = withSharedStyles(parseLSP(content, id), (file) => this.addWatchFile?.(file));
|
|
5296
|
-
const componentId = basename(id, ".lsp").toLowerCase();
|
|
5331
|
+
const componentId = [...componentFiles].find(([, file]) => resolve4(root, file) === resolve4(id))?.[0] ?? basename(id, ".lsp").toLowerCase();
|
|
5297
5332
|
let compiledLogic = parsed.logic;
|
|
5298
5333
|
if (useTypeScript && parsed.logic.trim()) {
|
|
5299
5334
|
compiledLogic = await project.compileLogic(parsed.logic);
|
|
5300
5335
|
}
|
|
5301
|
-
const registryObj =
|
|
5336
|
+
const registryObj = project.registryFor(componentId);
|
|
5302
5337
|
const transformedTemplate = transformTemplate(parsed.view, componentId, registryObj);
|
|
5303
5338
|
const transformedSkeleton = parsed.skeleton ? transformTemplate(parsed.skeleton, componentId, registryObj) : "";
|
|
5304
5339
|
const scopeId = scopeIdFor(componentId);
|
|
@@ -5353,6 +5388,8 @@ ${entries.join(`,
|
|
|
5353
5388
|
}
|
|
5354
5389
|
for (const file of await findFragments())
|
|
5355
5390
|
server.watcher.add(file);
|
|
5391
|
+
if (host)
|
|
5392
|
+
return;
|
|
5356
5393
|
const proxied = Object.keys(server.config.server.proxy ?? {});
|
|
5357
5394
|
const isProxied = (pathname) => proxied.some((key) => key.startsWith("^") ? new RegExp(key).test(pathname) : pathname.startsWith(key));
|
|
5358
5395
|
const missing = (res) => {
|
|
@@ -5385,6 +5422,8 @@ ${entries.join(`,
|
|
|
5385
5422
|
});
|
|
5386
5423
|
},
|
|
5387
5424
|
configurePreviewServer(server) {
|
|
5425
|
+
if (host)
|
|
5426
|
+
return;
|
|
5388
5427
|
const ready = buildRouteTable();
|
|
5389
5428
|
server.middlewares.use(async (req, res, next) => {
|
|
5390
5429
|
const url = req.url;
|
|
@@ -5446,13 +5485,27 @@ ${entries.join(`,
|
|
|
5446
5485
|
const registryObj = Object.fromEntries(componentRegistry);
|
|
5447
5486
|
const templates = {};
|
|
5448
5487
|
for (const [componentId, raw] of componentRegistry) {
|
|
5449
|
-
templates[componentId] = transformTemplate(raw.template, componentId,
|
|
5488
|
+
templates[componentId] = transformTemplate(raw.template, componentId, project.registryFor(componentId));
|
|
5450
5489
|
}
|
|
5490
|
+
const flattens = !isDevMode && shouldRender && options.flattenStatic !== false;
|
|
5491
|
+
const definitions = flattens ? await project.definitions() : null;
|
|
5451
5492
|
const eager = new Set;
|
|
5452
5493
|
for (const page of await findPages(root)) {
|
|
5453
5494
|
const html = await readFile3(page, "utf-8");
|
|
5454
5495
|
const compiled = transformTemplate(html, "index", registryObj);
|
|
5455
|
-
|
|
5496
|
+
let markup = compiled;
|
|
5497
|
+
if (definitions) {
|
|
5498
|
+
try {
|
|
5499
|
+
markup = (await renderPage({
|
|
5500
|
+
html: compiled,
|
|
5501
|
+
definitions,
|
|
5502
|
+
css: cssMode,
|
|
5503
|
+
pageExpressions: buildExpressionTable(extractExpressions(compiled))
|
|
5504
|
+
})).html;
|
|
5505
|
+
} catch {
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
eagerComponents(markup, templates).forEach((id) => eager.add(id));
|
|
5456
5509
|
}
|
|
5457
5510
|
return [...eager];
|
|
5458
5511
|
}
|
|
@@ -5602,7 +5655,7 @@ ${entries.join(`,
|
|
|
5602
5655
|
return existsSync(join4(dir, ROUTES_DIR)) ? join4(dir, ROUTES_DIR) : dir;
|
|
5603
5656
|
}
|
|
5604
5657
|
function pageKey(file) {
|
|
5605
|
-
return relative4(root, file).split(
|
|
5658
|
+
return relative4(root, file).split(sep3).join("/");
|
|
5606
5659
|
}
|
|
5607
5660
|
async function findPages(dir) {
|
|
5608
5661
|
if (host)
|
|
@@ -5642,7 +5695,7 @@ ${entries.join(`,
|
|
|
5642
5695
|
routeTable.length = 0;
|
|
5643
5696
|
routeDeclarations.clear();
|
|
5644
5697
|
for (const file of await findPages(root)) {
|
|
5645
|
-
const rel = relative4(base, file).split(
|
|
5698
|
+
const rel = relative4(base, file).split(sep3).join("/");
|
|
5646
5699
|
const key = pageKey(file);
|
|
5647
5700
|
let declared = null;
|
|
5648
5701
|
try {
|
|
@@ -5717,9 +5770,9 @@ ${entries.join(`,
|
|
|
5717
5770
|
const absolute = resolve4(root, file);
|
|
5718
5771
|
const within = relative4(root, absolute);
|
|
5719
5772
|
if (!within.startsWith("..") && !isAbsolute(within)) {
|
|
5720
|
-
return "/" + within.split(
|
|
5773
|
+
return "/" + within.split(sep3).join("/");
|
|
5721
5774
|
}
|
|
5722
|
-
return absolute.split(
|
|
5775
|
+
return absolute.split(sep3).join("/");
|
|
5723
5776
|
}
|
|
5724
5777
|
}
|
|
5725
5778
|
function generateComponentModule(parsed, transformedTemplate, componentId, scopeId) {
|