@loadbare/app 0.6.0 → 0.7.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/build/assemble.js +1 -0
- package/dist/build/assemble.js.map +1 -0
- package/dist/build/cli.js +1 -0
- package/dist/build/cli.js.map +1 -0
- package/dist/build/elements.js +1 -0
- package/dist/build/elements.js.map +1 -0
- package/dist/build/expand.js +1 -0
- package/dist/build/expand.js.map +1 -0
- package/dist/build/format.js +1 -0
- package/dist/build/format.js.map +1 -0
- package/dist/build/locations.d.ts +2 -2
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +3 -4
- package/dist/build/locations.js.map +1 -0
- package/dist/build/origins.js +1 -0
- package/dist/build/origins.js.map +1 -0
- package/dist/build/package-root.js +1 -0
- package/dist/build/package-root.js.map +1 -0
- package/dist/build/pages.d.ts +4 -0
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +5 -0
- package/dist/build/pages.js.map +1 -0
- package/dist/build/styles.js +1 -0
- package/dist/build/styles.js.map +1 -0
- package/dist/core/lb-constants.js +1 -0
- package/dist/core/lb-constants.js.map +1 -0
- package/dist/core/lb-types.d.ts +1 -1
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +2 -1
- package/dist/core/lb-types.js.map +1 -0
- package/dist/hub/lb-apply.d.ts +11 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +50 -13
- package/dist/hub/lb-apply.js.map +1 -0
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +117 -109
- package/dist/hub/lb-hub.browser.js.map +1 -0
- package/dist/server/lb-express.d.ts +1 -1
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +1 -0
- package/dist/server/lb-express.js.map +1 -0
- package/dist/server/lb-server.d.ts +1 -1
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +2 -1
- package/dist/server/lb-server.js.map +1 -0
- package/docs/TECHREF-1.0.md +149 -42
- package/docs/analysis-accidental-complexity.md +149 -0
- package/docs/reference/custom-elements.md +10 -17
- package/docs/reference/data-binding.md +56 -27
- package/docs/reference/server.md +18 -0
- package/docs/reference/widgets.md +3 -3
- package/docs/theory.md +114 -1
- package/docs/tutorials/072-inserting-into-a-list.md +14 -11
- package/docs/tutorials/080-widget-requests.md +9 -26
- package/package.json +2 -2
- package/dist/tests/assemble.test.d.ts +0 -8
- package/dist/tests/assemble.test.d.ts.map +0 -1
- package/dist/tests/assemble.test.js +0 -210
- package/dist/tests/elements.test.d.ts +0 -8
- package/dist/tests/elements.test.d.ts.map +0 -1
- package/dist/tests/elements.test.js +0 -118
- package/dist/tests/expand.test.d.ts +0 -10
- package/dist/tests/expand.test.d.ts.map +0 -1
- package/dist/tests/expand.test.js +0 -253
- package/dist/tests/fixtures/elements/collision/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/collision/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/imports.js +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/local/widgets/app-box.browser.js +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +0 -3
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts +0 -5
- package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/manifest-not-array/imports.js +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts +0 -2
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/pkg/acme-widget.browser.js +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts +0 -6
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts.map +0 -1
- package/dist/tests/fixtures/elements/unmarked/widgets/app-box.js +0 -1
- package/dist/tests/helpers/console.d.ts +0 -20
- package/dist/tests/helpers/console.d.ts.map +0 -1
- package/dist/tests/helpers/console.js +0 -28
- package/dist/tests/helpers/dom.d.ts +0 -18
- package/dist/tests/helpers/dom.d.ts.map +0 -1
- package/dist/tests/helpers/dom.js +0 -22
- package/dist/tests/helpers/hub.d.ts +0 -73
- package/dist/tests/helpers/hub.d.ts.map +0 -1
- package/dist/tests/helpers/hub.js +0 -151
- package/dist/tests/lb-apply.test.d.ts +0 -8
- package/dist/tests/lb-apply.test.d.ts.map +0 -1
- package/dist/tests/lb-apply.test.js +0 -177
- package/dist/tests/lb-express.test.d.ts +0 -14
- package/dist/tests/lb-express.test.d.ts.map +0 -1
- package/dist/tests/lb-express.test.js +0 -243
- package/dist/tests/lb-hub.test.d.ts +0 -14
- package/dist/tests/lb-hub.test.d.ts.map +0 -1
- package/dist/tests/lb-hub.test.js +0 -319
- package/dist/tests/lb-list.test.d.ts +0 -12
- package/dist/tests/lb-list.test.d.ts.map +0 -1
- package/dist/tests/lb-list.test.js +0 -339
- package/dist/tests/lb-server.test.d.ts +0 -9
- package/dist/tests/lb-server.test.d.ts.map +0 -1
- package/dist/tests/lb-server.test.js +0 -546
- package/dist/tests/origins.test.d.ts +0 -10
- package/dist/tests/origins.test.d.ts.map +0 -1
- package/dist/tests/origins.test.js +0 -387
- package/dist/tests/pages.test.d.ts +0 -6
- package/dist/tests/pages.test.d.ts.map +0 -1
- package/dist/tests/pages.test.js +0 -148
- package/dist/tests/styles.test.d.ts +0 -7
- package/dist/tests/styles.test.d.ts.map +0 -1
- package/dist/tests/styles.test.js +0 -76
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The origin cascade — see build/origins.ts.
|
|
3
|
-
*
|
|
4
|
-
* Definitions, scripts and stylesheets all come from the same ordered list of
|
|
5
|
-
* origins, so they are all tested here: that one origin is scanned the same
|
|
6
|
-
* way whatever it is, that a duplicate tag inside one origin is an error, and
|
|
7
|
-
* that a later origin wins over an earlier one.
|
|
8
|
-
*/
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { describe, it } from "node:test";
|
|
11
|
-
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
12
|
-
import { tmpdir } from "node:os";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
import { buildOrigins, cssFrom, scanAll, scanOrigin } from "../build/origins";
|
|
15
|
-
async function tree(files) {
|
|
16
|
-
const dir = await mkdtemp(path.join(tmpdir(), "loadbare-origins-"));
|
|
17
|
-
for (const [name, content] of Object.entries(files)) {
|
|
18
|
-
const file = path.join(dir, name);
|
|
19
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
20
|
-
await writeFile(file, content, "utf-8");
|
|
21
|
-
}
|
|
22
|
-
return dir;
|
|
23
|
-
}
|
|
24
|
-
/** A builtin with no files of its own, so a test sees only what it wrote. */
|
|
25
|
-
const NO_BUILTIN = {
|
|
26
|
-
builtinHubDir: path.join(tmpdir(), "loadbare-absent-hub"),
|
|
27
|
-
packageName: "@loadbare/app",
|
|
28
|
-
};
|
|
29
|
-
const base = (name) => path.basename(name);
|
|
30
|
-
describe("scanOrigin", () => {
|
|
31
|
-
it("classifies by filename: a tag-shaped .html, .browser.ts, and any .css", async () => {
|
|
32
|
-
const dir = await tree({
|
|
33
|
-
"app-box.html": "<div lb-slot></div>",
|
|
34
|
-
"app-box.browser.ts": "export {};",
|
|
35
|
-
"theme.css": "* {}",
|
|
36
|
-
});
|
|
37
|
-
try {
|
|
38
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
39
|
-
assert.deepEqual([...scan.definitions.keys()], ["app-box"]);
|
|
40
|
-
assert.deepEqual([...scan.scripts.keys()], ["app-box"]);
|
|
41
|
-
assert.deepEqual(scan.css.map(base), ["theme.css"]);
|
|
42
|
-
}
|
|
43
|
-
finally {
|
|
44
|
-
await rm(dir, { recursive: true, force: true });
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
it("leaves a module alone when its name does not say .browser", async () => {
|
|
48
|
-
const dir = await tree({
|
|
49
|
-
"app-box.html": "<div lb-slot></div>",
|
|
50
|
-
"secrets.ts": "export const KEY = 'shh';",
|
|
51
|
-
"app-box.ts": "export const KEY = 'shh';",
|
|
52
|
-
});
|
|
53
|
-
try {
|
|
54
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
55
|
-
assert.equal(scan.scripts.size, 0);
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
await rm(dir, { recursive: true, force: true });
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
it("remembers an unmarked module named after a tag, so the error can name it", async () => {
|
|
62
|
-
const dir = await tree({
|
|
63
|
-
"app-box.ts": "export {};",
|
|
64
|
-
"helpers.ts": "export {};",
|
|
65
|
-
"app-box.d.ts": "export {};",
|
|
66
|
-
});
|
|
67
|
-
try {
|
|
68
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
69
|
-
assert.deepEqual([...scan.unmarked.keys()], ["app-box"]);
|
|
70
|
-
assert.match(scan.unmarked.get("app-box"), /app-box\.ts$/);
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
await rm(dir, { recursive: true, force: true });
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
it("takes the marked file when both names are present", async () => {
|
|
77
|
-
const dir = await tree({
|
|
78
|
-
"app-box.ts": "export {};",
|
|
79
|
-
"app-box.browser.ts": "export {};",
|
|
80
|
-
});
|
|
81
|
-
try {
|
|
82
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
83
|
-
assert.match(scan.scripts.get("app-box"), /app-box\.browser$/);
|
|
84
|
-
assert.match(scan.unmarked.get("app-box"), /app-box\.ts$/);
|
|
85
|
-
}
|
|
86
|
-
finally {
|
|
87
|
-
await rm(dir, { recursive: true, force: true });
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
it("ignores files whose basename is not a tag", async () => {
|
|
91
|
-
const dir = await tree({
|
|
92
|
-
"chrome.html": "<lb-hub><main></main></lb-hub>",
|
|
93
|
-
"about.page.html": "<h1>About</h1>",
|
|
94
|
-
"about.requests.ts": "export const requests = {};",
|
|
95
|
-
"imports.ts": "export default {};",
|
|
96
|
-
});
|
|
97
|
-
try {
|
|
98
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
99
|
-
assert.equal(scan.definitions.size, 0);
|
|
100
|
-
assert.equal(scan.scripts.size, 0);
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
await rm(dir, { recursive: true, force: true });
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
it("finds files at any depth, and sorts css by filename not directory", async () => {
|
|
107
|
-
const dir = await tree({
|
|
108
|
-
"deep/nested/app-box.browser.ts": "export {};",
|
|
109
|
-
"widgets/app-box.css": ".b {}",
|
|
110
|
-
"00-reset.css": "* {}",
|
|
111
|
-
});
|
|
112
|
-
try {
|
|
113
|
-
const scan = await scanOrigin({ name: "src", dirs: [dir] });
|
|
114
|
-
assert.deepEqual([...scan.scripts.keys()], ["app-box"]);
|
|
115
|
-
assert.deepEqual(scan.css.map(base), ["00-reset.css", "app-box.css"]);
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
await rm(dir, { recursive: true, force: true });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
it("throws when a definition filename carries capitals", async () => {
|
|
122
|
-
const dir = await tree({ "App-Box.html": "<div></div>" });
|
|
123
|
-
try {
|
|
124
|
-
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /carries capitals.*must be named 'app-box\.html'/);
|
|
125
|
-
}
|
|
126
|
-
finally {
|
|
127
|
-
await rm(dir, { recursive: true, force: true });
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
it("throws when a widget script filename carries capitals", async () => {
|
|
131
|
-
const dir = await tree({ "App-Box.browser.ts": "export {};" });
|
|
132
|
-
try {
|
|
133
|
-
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /must be named 'app-box\.browser\.ts'/);
|
|
134
|
-
}
|
|
135
|
-
finally {
|
|
136
|
-
await rm(dir, { recursive: true, force: true });
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
it("throws when one origin has two definitions for the same tag", async () => {
|
|
140
|
-
const dir = await tree({
|
|
141
|
-
"a/app-box.html": "<div></div>",
|
|
142
|
-
"b/app-box.html": "<span></span>",
|
|
143
|
-
});
|
|
144
|
-
try {
|
|
145
|
-
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /'src' has two definition files for <app-box>/);
|
|
146
|
-
}
|
|
147
|
-
finally {
|
|
148
|
-
await rm(dir, { recursive: true, force: true });
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
it("throws when one origin has two scripts for the same tag", async () => {
|
|
152
|
-
const dir = await tree({
|
|
153
|
-
"a/app-box.browser.ts": "export {};",
|
|
154
|
-
"b/app-box.browser.ts": "export {};",
|
|
155
|
-
});
|
|
156
|
-
try {
|
|
157
|
-
await assert.rejects(() => scanOrigin({ name: "src", dirs: [dir] }), /'src' has two script files for <app-box>/);
|
|
158
|
-
}
|
|
159
|
-
finally {
|
|
160
|
-
await rm(dir, { recursive: true, force: true });
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
it("takes a compiled .js as a script, the way a published package ships one", async () => {
|
|
164
|
-
const dir = await tree({
|
|
165
|
-
"acme-widget.html": "<div lb-slot></div>",
|
|
166
|
-
"acme-widget.browser.js": "export {};",
|
|
167
|
-
"acme-widget.browser.d.ts": "export {};",
|
|
168
|
-
});
|
|
169
|
-
try {
|
|
170
|
-
const scan = await scanOrigin({ name: "@acme/widgets", dirs: [dir] });
|
|
171
|
-
assert.deepEqual([...scan.definitions.keys()], ["acme-widget"]);
|
|
172
|
-
// The .d.ts is not a second script: stripping ".ts" leaves a dot.
|
|
173
|
-
assert.deepEqual([...scan.scripts.keys()], ["acme-widget"]);
|
|
174
|
-
assert.match(scan.scripts.get("acme-widget"), /acme-widget\.browser$/);
|
|
175
|
-
}
|
|
176
|
-
finally {
|
|
177
|
-
await rm(dir, { recursive: true, force: true });
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
describe("buildOrigins", () => {
|
|
182
|
-
it("orders the built-in first, then packages as declared, then src", async () => {
|
|
183
|
-
const src = await tree({
|
|
184
|
-
"node_modules/pkg-a/package.json": "{}",
|
|
185
|
-
"node_modules/pkg-b/package.json": "{}",
|
|
186
|
-
});
|
|
187
|
-
try {
|
|
188
|
-
const origins = await buildOrigins(NO_BUILTIN, ["pkg-b", "pkg-a"], src);
|
|
189
|
-
assert.deepEqual(origins.map((o) => o.name), ["@loadbare/app", "pkg-b", "pkg-a", src]);
|
|
190
|
-
}
|
|
191
|
-
finally {
|
|
192
|
-
await rm(src, { recursive: true, force: true });
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
it("throws when a declared package is not installed", async () => {
|
|
196
|
-
const src = await tree({});
|
|
197
|
-
try {
|
|
198
|
-
await assert.rejects(() => buildOrigins(NO_BUILTIN, ["nowhere-pkg"], src), /could not find installed package 'nowhere-pkg'/);
|
|
199
|
-
}
|
|
200
|
-
finally {
|
|
201
|
-
await rm(src, { recursive: true, force: true });
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
it("walks up from src to find node_modules at the application root", async () => {
|
|
205
|
-
const src = await tree({
|
|
206
|
-
"node_modules/pkg-a/package.json": "{}",
|
|
207
|
-
"node_modules/pkg-a/style.css": "a {}",
|
|
208
|
-
"app/src/chrome.html": "<lb-hub><main></main></lb-hub>",
|
|
209
|
-
});
|
|
210
|
-
try {
|
|
211
|
-
const inner = path.join(src, "app", "src");
|
|
212
|
-
const origins = await buildOrigins(NO_BUILTIN, ["pkg-a"], inner);
|
|
213
|
-
const scans = await Promise.all(origins.map(scanOrigin));
|
|
214
|
-
assert.deepEqual(cssFrom(scans).map(base), ["style.css"]);
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
await rm(src, { recursive: true, force: true });
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
describe("a package's declared widget directory", () => {
|
|
222
|
-
/**
|
|
223
|
-
* The published shape this exists for: compiled output beside the source it
|
|
224
|
-
* was built from. Scanning the whole package finds both and calls the tag
|
|
225
|
-
* a duplicate; scanning what the package declared finds the one that ships.
|
|
226
|
-
*/
|
|
227
|
-
it("scans only loadbare.widgets, so source beside dist is not a duplicate", async () => {
|
|
228
|
-
const src = await tree({
|
|
229
|
-
"node_modules/acme/package.json": JSON.stringify({
|
|
230
|
-
loadbare: { widgets: "./dist" },
|
|
231
|
-
}),
|
|
232
|
-
"node_modules/acme/dist/acme-widget.browser.js": "export {};",
|
|
233
|
-
"node_modules/acme/dist/acme-widget.html": "<b lb-slot></b>",
|
|
234
|
-
"node_modules/acme/src/acme-widget.browser.ts": "export {};",
|
|
235
|
-
"node_modules/acme/src/acme-widget.html": "<b lb-slot></b>",
|
|
236
|
-
"node_modules/acme/README.md": "# acme",
|
|
237
|
-
"imports.ts": `export default ["acme"];`,
|
|
238
|
-
});
|
|
239
|
-
try {
|
|
240
|
-
const scans = await scanAll(NO_BUILTIN, src, path.join(src, "imports.ts"));
|
|
241
|
-
const acme = scans.find((scan) => scan.name === "acme");
|
|
242
|
-
assert.deepEqual([...acme.scripts.keys()], ["acme-widget"]);
|
|
243
|
-
assert.match(acme.scripts.get("acme-widget"), /dist[/\\]acme-widget\.browser$/);
|
|
244
|
-
assert.match(acme.definitions.get("acme-widget"), /dist[/\\]acme-widget\.html$/);
|
|
245
|
-
}
|
|
246
|
-
finally {
|
|
247
|
-
await rm(src, { recursive: true, force: true });
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
it("scans the whole package when it declares nothing", async () => {
|
|
251
|
-
const src = await tree({
|
|
252
|
-
"node_modules/acme/package.json": "{}",
|
|
253
|
-
"node_modules/acme/widgets/acme-widget.browser.ts": "export {};",
|
|
254
|
-
});
|
|
255
|
-
try {
|
|
256
|
-
const origins = await buildOrigins(NO_BUILTIN, ["acme"], src);
|
|
257
|
-
const scans = await Promise.all(origins.map(scanOrigin));
|
|
258
|
-
assert.deepEqual([...scans[1].scripts.keys()], ["acme-widget"]);
|
|
259
|
-
}
|
|
260
|
-
finally {
|
|
261
|
-
await rm(src, { recursive: true, force: true });
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
it("refuses a declared directory the installed package does not have", async () => {
|
|
265
|
-
const src = await tree({
|
|
266
|
-
"node_modules/acme/package.json": JSON.stringify({
|
|
267
|
-
loadbare: { widgets: "./dist" },
|
|
268
|
-
}),
|
|
269
|
-
"node_modules/acme/widgets/acme-widget.browser.ts": "export {};",
|
|
270
|
-
});
|
|
271
|
-
try {
|
|
272
|
-
await assert.rejects(() => buildOrigins(NO_BUILTIN, ["acme"], src), /'acme' declares widgets at '\.\/dist', which is not in the installed package/);
|
|
273
|
-
}
|
|
274
|
-
finally {
|
|
275
|
-
await rm(src, { recursive: true, force: true });
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
it("refuses a declared directory outside the package", async () => {
|
|
279
|
-
const src = await tree({
|
|
280
|
-
"node_modules/acme/package.json": JSON.stringify({
|
|
281
|
-
loadbare: { widgets: "../../src" },
|
|
282
|
-
}),
|
|
283
|
-
});
|
|
284
|
-
try {
|
|
285
|
-
await assert.rejects(() => buildOrigins(NO_BUILTIN, ["acme"], src), /which is outside the package/);
|
|
286
|
-
}
|
|
287
|
-
finally {
|
|
288
|
-
await rm(src, { recursive: true, force: true });
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
it("refuses a loadbare.widgets that is not a path", async () => {
|
|
292
|
-
const src = await tree({
|
|
293
|
-
"node_modules/acme/package.json": JSON.stringify({
|
|
294
|
-
loadbare: { widgets: ["./dist"] },
|
|
295
|
-
}),
|
|
296
|
-
});
|
|
297
|
-
try {
|
|
298
|
-
await assert.rejects(() => buildOrigins(NO_BUILTIN, ["acme"], src), /is not a path/);
|
|
299
|
-
}
|
|
300
|
-
finally {
|
|
301
|
-
await rm(src, { recursive: true, force: true });
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
describe("the css cascade", () => {
|
|
306
|
-
it("collects one package's CSS, sorted by filename", async () => {
|
|
307
|
-
const src = await tree({
|
|
308
|
-
"node_modules/acme-widgets/package.json": "{}",
|
|
309
|
-
"node_modules/acme-widgets/z.css": "z {}",
|
|
310
|
-
"node_modules/acme-widgets/a.css": "a {}",
|
|
311
|
-
"node_modules/acme-widgets/dist/00-reset.css": "* { margin: 0; }",
|
|
312
|
-
"imports.ts": `export default ["acme-widgets"];`,
|
|
313
|
-
});
|
|
314
|
-
try {
|
|
315
|
-
const scans = await scanAll(NO_BUILTIN, src, path.join(src, "imports.ts"));
|
|
316
|
-
assert.deepEqual(cssFrom(scans).map(base), [
|
|
317
|
-
"00-reset.css",
|
|
318
|
-
"a.css",
|
|
319
|
-
"z.css",
|
|
320
|
-
]);
|
|
321
|
-
}
|
|
322
|
-
finally {
|
|
323
|
-
await rm(src, { recursive: true, force: true });
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
it("keeps packages in declaration order, without interleaving their files", async () => {
|
|
327
|
-
const src = await tree({
|
|
328
|
-
"node_modules/pkg-a/package.json": "{}",
|
|
329
|
-
"node_modules/pkg-a/a-only.css": "a {}",
|
|
330
|
-
"node_modules/pkg-b/package.json": "{}",
|
|
331
|
-
"node_modules/pkg-b/aaa-early.css": "b {}",
|
|
332
|
-
"imports.ts": `export default ["pkg-b", "pkg-a"];`,
|
|
333
|
-
});
|
|
334
|
-
try {
|
|
335
|
-
const scans = await scanAll(NO_BUILTIN, src, path.join(src, "imports.ts"));
|
|
336
|
-
assert.deepEqual(cssFrom(scans).map(base), [
|
|
337
|
-
"aaa-early.css",
|
|
338
|
-
"a-only.css",
|
|
339
|
-
]);
|
|
340
|
-
}
|
|
341
|
-
finally {
|
|
342
|
-
await rm(src, { recursive: true, force: true });
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
it("puts the application's own css last, after every package's", async () => {
|
|
346
|
-
const src = await tree({
|
|
347
|
-
"node_modules/pkg-a/package.json": "{}",
|
|
348
|
-
"node_modules/pkg-a/zzz-late.css": "a {}",
|
|
349
|
-
"00-reset.css": "* {}",
|
|
350
|
-
"imports.ts": `export default ["pkg-a"];`,
|
|
351
|
-
});
|
|
352
|
-
try {
|
|
353
|
-
const scans = await scanAll(NO_BUILTIN, src, path.join(src, "imports.ts"));
|
|
354
|
-
assert.deepEqual(cssFrom(scans).map(base), [
|
|
355
|
-
"zzz-late.css",
|
|
356
|
-
"00-reset.css",
|
|
357
|
-
]);
|
|
358
|
-
}
|
|
359
|
-
finally {
|
|
360
|
-
await rm(src, { recursive: true, force: true });
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
it("ignores a package's own nested node_modules", async () => {
|
|
364
|
-
const src = await tree({
|
|
365
|
-
"node_modules/pkg-a/package.json": "{}",
|
|
366
|
-
"node_modules/pkg-a/style.css": "a {}",
|
|
367
|
-
"node_modules/pkg-a/node_modules/transitive/style.css": "t {}",
|
|
368
|
-
"imports.ts": `export default ["pkg-a"];`,
|
|
369
|
-
});
|
|
370
|
-
try {
|
|
371
|
-
const scans = await scanAll(NO_BUILTIN, src, path.join(src, "imports.ts"));
|
|
372
|
-
assert.deepEqual(cssFrom(scans).map(base), ["style.css"]);
|
|
373
|
-
}
|
|
374
|
-
finally {
|
|
375
|
-
await rm(src, { recursive: true, force: true });
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
it("returns nothing when there is no css anywhere", async () => {
|
|
379
|
-
const src = await tree({ "chrome.html": "<lb-hub><main></main></lb-hub>" });
|
|
380
|
-
try {
|
|
381
|
-
assert.deepEqual(cssFrom(await scanAll(NO_BUILTIN, src)), []);
|
|
382
|
-
}
|
|
383
|
-
finally {
|
|
384
|
-
await rm(src, { recursive: true, force: true });
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pages.test.d.ts","sourceRoot":"","sources":["../../tests/pages.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/tests/pages.test.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discovering `*.requests.ts`/`*.queries.ts` pairs (build/locations.ts) and
|
|
3
|
-
* generating the hub registry from them (build/pages.ts).
|
|
4
|
-
*/
|
|
5
|
-
import assert from "node:assert/strict";
|
|
6
|
-
import { describe, it } from "node:test";
|
|
7
|
-
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
8
|
-
import { tmpdir } from "node:os";
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
import { resolveLocations } from "../build/locations";
|
|
11
|
-
import { pagesSource } from "../build/pages";
|
|
12
|
-
const CHROME = `<!doctype html><body><lb-hub><main></main></lb-hub></body>`;
|
|
13
|
-
async function tree(files) {
|
|
14
|
-
const dir = await mkdtemp(path.join(tmpdir(), "loadbare-pages-"));
|
|
15
|
-
for (const [name, content] of Object.entries(files)) {
|
|
16
|
-
const file = path.join(dir, name);
|
|
17
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
18
|
-
await writeFile(file, content, "utf-8");
|
|
19
|
-
}
|
|
20
|
-
return dir;
|
|
21
|
-
}
|
|
22
|
-
describe("discovering requests and queries", () => {
|
|
23
|
-
it("pairs a *.requests.ts and *.queries.ts with the page sharing their basename", async () => {
|
|
24
|
-
const src = await tree({
|
|
25
|
-
"chrome.html": CHROME,
|
|
26
|
-
"pages/hello.page.html": `<p>hi</p>`,
|
|
27
|
-
"pages/hello.requests.ts": `export const requests = {};`,
|
|
28
|
-
"pages/hello.queries.ts": `export const queries = {};`,
|
|
29
|
-
});
|
|
30
|
-
try {
|
|
31
|
-
const { pageSources } = await resolveLocations({
|
|
32
|
-
src,
|
|
33
|
-
out: `${src}/dist`,
|
|
34
|
-
});
|
|
35
|
-
const hello = pageSources.get("hello");
|
|
36
|
-
assert.ok(hello?.requests?.endsWith("hello.requests"));
|
|
37
|
-
assert.ok(hello?.queries?.endsWith("hello.queries"));
|
|
38
|
-
}
|
|
39
|
-
finally {
|
|
40
|
-
await rm(src, { recursive: true, force: true });
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
it("allows a page to have only one of the two", async () => {
|
|
44
|
-
const src = await tree({
|
|
45
|
-
"chrome.html": CHROME,
|
|
46
|
-
"pages/counter.page.html": `<p>0</p>`,
|
|
47
|
-
"pages/counter.queries.ts": `export const queries = {};`,
|
|
48
|
-
});
|
|
49
|
-
try {
|
|
50
|
-
const { pageSources } = await resolveLocations({
|
|
51
|
-
src,
|
|
52
|
-
out: `${src}/dist`,
|
|
53
|
-
});
|
|
54
|
-
const counter = pageSources.get("counter");
|
|
55
|
-
assert.equal(counter?.requests, undefined);
|
|
56
|
-
assert.ok(counter?.queries?.endsWith("counter.queries"));
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
await rm(src, { recursive: true, force: true });
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
it("throws when no chrome file is found", async () => {
|
|
63
|
-
const src = await tree({ "pages/hello.page.html": `<p>hi</p>` });
|
|
64
|
-
try {
|
|
65
|
-
await assert.rejects(() => resolveLocations({ src, out: `${src}/dist` }), /no chrome\.html file found/);
|
|
66
|
-
}
|
|
67
|
-
finally {
|
|
68
|
-
await rm(src, { recursive: true, force: true });
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
it("throws when two chrome files are found", async () => {
|
|
72
|
-
const src = await tree({
|
|
73
|
-
"chrome.html": CHROME,
|
|
74
|
-
"other/chrome.html": CHROME,
|
|
75
|
-
});
|
|
76
|
-
try {
|
|
77
|
-
await assert.rejects(() => resolveLocations({ src, out: `${src}/dist` }), /more than one chrome\.html file found/);
|
|
78
|
-
}
|
|
79
|
-
finally {
|
|
80
|
-
await rm(src, { recursive: true, force: true });
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
it("throws when two imports.ts files are found", async () => {
|
|
84
|
-
const src = await tree({
|
|
85
|
-
"chrome.html": CHROME,
|
|
86
|
-
"imports.ts": `export default [];`,
|
|
87
|
-
"widgets/imports.ts": `export default [];`,
|
|
88
|
-
});
|
|
89
|
-
try {
|
|
90
|
-
await assert.rejects(() => resolveLocations({ src, out: `${src}/dist` }), /more than one imports\.ts found/);
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
await rm(src, { recursive: true, force: true });
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
it("throws when two page files in different directories share a stub", async () => {
|
|
97
|
-
const src = await tree({
|
|
98
|
-
"chrome.html": CHROME,
|
|
99
|
-
"pages/members.page.html": `<p>one</p>`,
|
|
100
|
-
"admin/members.page.html": `<p>two</p>`,
|
|
101
|
-
});
|
|
102
|
-
try {
|
|
103
|
-
await assert.rejects(() => resolveLocations({ src, out: `${src}/dist` }), /more than one 'members\.page\.html'/);
|
|
104
|
-
}
|
|
105
|
-
finally {
|
|
106
|
-
await rm(src, { recursive: true, force: true });
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
it("throws when a requests or queries file has no matching page", async () => {
|
|
110
|
-
const src = await tree({
|
|
111
|
-
"chrome.html": CHROME,
|
|
112
|
-
"pages/orphan.requests.ts": `export const requests = {};`,
|
|
113
|
-
});
|
|
114
|
-
try {
|
|
115
|
-
await assert.rejects(() => resolveLocations({ src, out: `${src}/dist` }), /no 'orphan\.page\.html'/);
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
await rm(src, { recursive: true, force: true });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
describe("pagesSource", () => {
|
|
123
|
-
it("returns undefined when no page has requests or queries", () => {
|
|
124
|
-
assert.equal(pagesSource([{ name: "hello", file: "/src/hello.page.html" }], new Map(), "/out", "@loadbare/app"), undefined);
|
|
125
|
-
});
|
|
126
|
-
it("generates one import per file found and one entry per page", () => {
|
|
127
|
-
const pages = [
|
|
128
|
-
{ name: "hello", file: "/src/hello.page.html" },
|
|
129
|
-
{ name: "counter", file: "/src/counter.page.html" },
|
|
130
|
-
];
|
|
131
|
-
const pageSources = new Map([
|
|
132
|
-
[
|
|
133
|
-
"hello",
|
|
134
|
-
{ requests: "/src/hello.requests", queries: "/src/hello.queries" },
|
|
135
|
-
],
|
|
136
|
-
["counter", { queries: "/src/counter.queries" }],
|
|
137
|
-
]);
|
|
138
|
-
const out = pagesSource(pages, pageSources, "/out", "@loadbare/app");
|
|
139
|
-
assert.match(out, /import \{ createHub \} from "@loadbare\/app\/server";/);
|
|
140
|
-
assert.match(out, /import \{ requests as page_hello_Requests \} from "\.\.\/src\/hello\.requests";/);
|
|
141
|
-
assert.match(out, /import \{ queries as page_hello_Queries \} from "\.\.\/src\/hello\.queries";/);
|
|
142
|
-
assert.match(out, /import \{ queries as page_counter_Queries \} from "\.\.\/src\/counter\.queries";/);
|
|
143
|
-
assert.doesNotMatch(out, /page_counter_Requests/);
|
|
144
|
-
assert.match(out, /"hello": \{ requests: page_hello_Requests, queries: page_hello_Queries \}/);
|
|
145
|
-
assert.match(out, /"counter": \{ requests: \{\}, queries: page_counter_Queries \}/);
|
|
146
|
-
assert.match(out, /export const hub = createHub\(\{/);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"styles.test.d.ts","sourceRoot":"","sources":["../../tests/styles.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discovering `.css` files (build/locations.ts) and concatenating them
|
|
3
|
-
* (build/styles.ts) — see that file's header for what ordering guarantee is
|
|
4
|
-
* and is not made.
|
|
5
|
-
*/
|
|
6
|
-
import assert from "node:assert/strict";
|
|
7
|
-
import { describe, it } from "node:test";
|
|
8
|
-
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
9
|
-
import { tmpdir } from "node:os";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { resolveLocations } from "../build/locations";
|
|
12
|
-
import { cssFrom, scanAll } from "../build/origins";
|
|
13
|
-
import { stylesSource } from "../build/styles";
|
|
14
|
-
const CHROME = `<!doctype html><body><lb-hub><main></main></lb-hub></body>`;
|
|
15
|
-
async function tree(files) {
|
|
16
|
-
const dir = await mkdtemp(path.join(tmpdir(), "loadbare-styles-"));
|
|
17
|
-
for (const [name, content] of Object.entries(files)) {
|
|
18
|
-
const file = path.join(dir, name);
|
|
19
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
20
|
-
await writeFile(file, content, "utf-8");
|
|
21
|
-
}
|
|
22
|
-
return dir;
|
|
23
|
-
}
|
|
24
|
-
describe("discovering CSS", () => {
|
|
25
|
-
it("finds a .css file anywhere under src", async () => {
|
|
26
|
-
const src = await tree({
|
|
27
|
-
"chrome.html": CHROME,
|
|
28
|
-
"widgets/app-box.css": ".app-box {}",
|
|
29
|
-
"styles/00-reset.css": "* { margin: 0; }",
|
|
30
|
-
});
|
|
31
|
-
try {
|
|
32
|
-
const locations = await resolveLocations({ src, out: `${src}/dist` });
|
|
33
|
-
const cssFiles = cssFrom(await scanAll(locations, src, locations.importsFile));
|
|
34
|
-
assert.equal(cssFiles.length, 2);
|
|
35
|
-
}
|
|
36
|
-
finally {
|
|
37
|
-
await rm(src, { recursive: true, force: true });
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
it("orders by filename, not by directory, so a global sorts first by naming itself 00-", async () => {
|
|
41
|
-
const src = await tree({
|
|
42
|
-
"chrome.html": CHROME,
|
|
43
|
-
"widgets/app-box.css": ".app-box {}",
|
|
44
|
-
"00-reset.css": "* { margin: 0; }",
|
|
45
|
-
});
|
|
46
|
-
try {
|
|
47
|
-
const locations = await resolveLocations({ src, out: `${src}/dist` });
|
|
48
|
-
const cssFiles = cssFrom(await scanAll(locations, src, locations.importsFile));
|
|
49
|
-
assert.deepEqual(cssFiles.map((f) => path.basename(f)), ["00-reset.css", "app-box.css"]);
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
await rm(src, { recursive: true, force: true });
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe("stylesSource", () => {
|
|
57
|
-
it("returns undefined for no files", async () => {
|
|
58
|
-
assert.equal(await stylesSource([]), undefined);
|
|
59
|
-
});
|
|
60
|
-
it("concatenates file contents in the order given", async () => {
|
|
61
|
-
const src = await tree({
|
|
62
|
-
"a.css": "a {}",
|
|
63
|
-
"b.css": "b {}",
|
|
64
|
-
});
|
|
65
|
-
try {
|
|
66
|
-
const out = await stylesSource([
|
|
67
|
-
path.join(src, "a.css"),
|
|
68
|
-
path.join(src, "b.css"),
|
|
69
|
-
]);
|
|
70
|
-
assert.equal(out, "a {}\nb {}");
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
await rm(src, { recursive: true, force: true });
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
});
|