@remit/web-client 0.0.203 → 0.0.204

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/web-client",
3
- "version": "0.0.203",
3
+ "version": "0.0.204",
4
4
  "type": "module",
5
5
  "description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
6
6
  "exports": {
@@ -7,22 +7,52 @@
7
7
  * an image whose notice points at a `LICENSE` nobody staged. That is the one
8
8
  * failure mode a reader cannot see and a distributor is answerable for.
9
9
  *
10
- * The dictionary is shadowed rather than the real one damaged: node resolves
11
- * from the importer outwards, so a package planted in this directory's own
12
- * `node_modules` answers for `dictionary-en-gb` here and nowhere else. That
13
- * makes this file its own process — node's test runner gives each file one —
14
- * because module resolution is cached per process and a sibling test resolving
15
- * the real package first would decide which one this sees.
10
+ * The dictionary is shadowed inside this test's own temporary tree rather than
11
+ * anywhere on the shared resolution path: the plugin sources are copied in
12
+ * beside a private `node_modules`, so `require.resolve` from that copy answers
13
+ * for the shadow and nowhere else. Planting into `spellcheck/node_modules`
14
+ * instead would be visible to every other test file — node's runner isolates
15
+ * the module cache per process, never the filesystem, and runs files
16
+ * concurrently.
16
17
  */
17
18
 
18
19
  import assert from "node:assert/strict";
19
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
20
+ import {
21
+ copyFileSync,
22
+ mkdirSync,
23
+ mkdtempSync,
24
+ readdirSync,
25
+ rmSync,
26
+ writeFileSync,
27
+ } from "node:fs";
28
+ import { createRequire } from "node:module";
20
29
  import { tmpdir } from "node:os";
21
- import { join } from "node:path";
30
+ import { dirname, join } from "node:path";
22
31
  import { after, describe, it } from "node:test";
32
+ import { pathToFileURL } from "node:url";
23
33
 
24
- const shadowRoot = join(import.meta.dirname, "node_modules");
25
- const shadow = join(shadowRoot, "dictionary-en-gb");
34
+ const here = import.meta.dirname;
35
+ const tmpRoot = mkdtempSync(join(tmpdir(), "spellcheck-licence-"));
36
+ after(() => {
37
+ rmSync(tmpRoot, { recursive: true, force: true });
38
+ });
39
+
40
+ // The plugin under test, imported from inside the temporary tree so its
41
+ // package resolution starts at the tree's own `node_modules`.
42
+ const src = join(tmpRoot, "spellcheck");
43
+ const engineDir = join(tmpRoot, "engine");
44
+ mkdirSync(src, { recursive: true });
45
+ // Every local import of vite-plugin.ts must be listed here.
46
+ for (const name of ["vite-plugin.ts", "languages.ts"]) {
47
+ copyFileSync(join(here, name), join(src, name));
48
+ }
49
+
50
+ // A licence-less shadow of `dictionary-en-gb`, plus the real `dictionary-en`
51
+ // copied across for the case that does stage a licence.
52
+ const modules = join(tmpRoot, "node_modules");
53
+ const real = (name: string) =>
54
+ dirname(createRequire(import.meta.url).resolve(name));
55
+ const shadow = join(modules, "dictionary-en-gb");
26
56
  mkdirSync(shadow, { recursive: true });
27
57
  writeFileSync(
28
58
  join(shadow, "package.json"),
@@ -31,8 +61,26 @@ writeFileSync(
31
61
  writeFileSync(join(shadow, "index.js"), "");
32
62
  writeFileSync(join(shadow, "index.aff"), "SET UTF-8\n");
33
63
  writeFileSync(join(shadow, "index.dic"), "1\nword\n");
64
+ const realEn = real("dictionary-en");
65
+ const stagedEn = join(modules, "dictionary-en");
66
+ mkdirSync(stagedEn, { recursive: true });
67
+ for (const entry of [
68
+ "package.json",
69
+ "index.js",
70
+ "index.aff",
71
+ "index.dic",
72
+ ] as const) {
73
+ copyFileSync(join(realEn, entry), join(stagedEn, entry));
74
+ }
75
+ // The licence text under whatever name upstream ships it.
76
+ const licenceFile = readdirSync(realEn).find((entry) =>
77
+ /^licen[cs]e/i.test(entry),
78
+ );
79
+ assert.ok(licenceFile);
80
+ copyFileSync(join(realEn, licenceFile), join(stagedEn, licenceFile));
34
81
 
35
- const engineDir = mkdtempSync(join(tmpdir(), "spellcheck-engine-"));
82
+ // Pins read from beside the engine; the stub carries its own.
83
+ mkdirSync(engineDir, { recursive: true });
36
84
  for (const name of [
37
85
  "hunspell.wasm",
38
86
  "hunspell.mjs",
@@ -41,15 +89,15 @@ for (const name of [
41
89
  ]) {
42
90
  writeFileSync(join(engineDir, name), "stub");
43
91
  }
92
+ writeFileSync(
93
+ join(engineDir, "pin.env"),
94
+ ["HUNSPELL_VERSION=0.0.0-test", "HUNSPELL_SHA256=test"].join("\n"),
95
+ );
44
96
  process.env.REMIT_SPELLCHECK_ENGINE_DIR = engineDir;
45
97
 
46
- const { stageSpellcheck } = await import("./vite-plugin.ts");
47
-
48
- after(() => {
49
- rmSync(shadow, { recursive: true, force: true });
50
- rmSync(shadowRoot, { recursive: true, force: true });
51
- rmSync(engineDir, { recursive: true, force: true });
52
- });
98
+ const { stageSpellcheck }: typeof import("./vite-plugin.ts") = await import(
99
+ pathToFileURL(join(src, "vite-plugin.ts")).href
100
+ );
53
101
 
54
102
  describe("a dictionary that ships no licence text", () => {
55
103
  it("stops the build rather than staging it", () => {