@inkandswitch/patchwork 0.7.0 → 0.7.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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # @inkandswitch/patchwork
2
2
 
3
+ ## 0.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - e1374ad: Serve the `/packages/…` builtin URLs in dev. Import maps don't apply to worker
8
+ scripts, so code that starts one — `@automerge/automerge-repo`'s shared
9
+ subduction websocket worker, for instance — asks for the `/packages/…` path the
10
+ build emits. Nothing served those in dev, and the worker failed to fetch; the
11
+ dev server now redirects them to the same optimized dep the page's import map
12
+ points at.
13
+ - e1374ad: Say what's actually wrong when a `static` package declares a directory that
14
+ isn't there. The package resolved fine — its `"patchwork": {"static": …}` field
15
+ is what's wrong — and "static source not found" named neither the field nor the
16
+ path it pointed at. The error now quotes the declaration, gives the full path
17
+ that's missing, and says that a package publishing its static tree as the root
18
+ of its own tarball shouldn't set the field at all.
19
+
20
+ ## 0.7.1
21
+
22
+ ### Patch Changes
23
+
24
+ - 8b9206d: Don't copy `static` sources or write `build-info.json` when a dev server shuts
25
+ down. `closeBundle` runs then too, so stopping `vite` was filling `dist/` with
26
+ a copy of every static source.
27
+
3
28
  ## 0.7.0
4
29
 
5
30
  ### Minor Changes
@@ -42,13 +42,17 @@ export function buildInfoPlugin(options = {}) {
42
42
  return null;
43
43
  let root;
44
44
  let outDir;
45
+ let serve = false;
45
46
  return {
46
47
  name: "@patchwork/build-info",
47
48
  configResolved(config) {
48
49
  root = config.root;
49
50
  outDir = join(config.root, config.build.outDir);
51
+ serve = config.command === "serve";
50
52
  },
51
53
  async closeBundle() {
54
+ if (serve)
55
+ return;
52
56
  const sources = resolveStatic(options, root).map((source) => source.packageDirectory
53
57
  ? { from: source.from, ...describe(source.packageDirectory) }
54
58
  : { from: source.from, revision: revision(source.path) });
@@ -14,6 +14,7 @@ const stylesheets = {
14
14
  [PATCHWORK_CSS]: fileURLToPath(import.meta.resolve("@inkandswitch/patchwork/global.css")),
15
15
  [BOOTLOADER_CSS]: fileURLToPath(import.meta.resolve("@inkandswitch/patchwork-bootloader/global.css")),
16
16
  };
17
+ const builtinPaths = new Map(Object.entries(builtins).map(([id, fileName]) => [fileName, id]));
17
18
  /**
18
19
  * Workers are `type: "module"` scripts the browser fetches directly, so import
19
20
  * maps don't apply to them and their heavy imports have to resolve to real
@@ -92,6 +93,17 @@ export function devPlugin(options = {}) {
92
93
  }
93
94
  return;
94
95
  }
96
+ // A worker script is fetched by URL, and import maps don't apply to
97
+ // those, so code that starts one reaches for the /packages/… path the
98
+ // build emits. In dev those are the optimized deps the page's import
99
+ // map points at — same module, one URL over.
100
+ const builtin = builtinPaths.get(pathname);
101
+ if (builtin) {
102
+ response.statusCode = 302;
103
+ response.setHeader("Location", `/@id/${encodeURI(devDependencyId(builtin))}`);
104
+ response.end();
105
+ return;
106
+ }
95
107
  const binary = wasm.get(pathname);
96
108
  if (binary) {
97
109
  try {
@@ -7,7 +7,8 @@ import type { PatchworkVitePluginOptions } from "./patchwork-plugin.js";
7
7
  * `from` is either a package specifier or a path relative to the site root. A
8
8
  * package says where its static tree lives with a `"patchwork": {"static":
9
9
  * "static-dist"}` field in its own package.json; without one, the whole
10
- * package directory is mounted.
10
+ * package directory is mounted — which is what a package that publishes its
11
+ * static tree as the root of its own tarball wants.
11
12
  */
12
13
  export interface PatchworkStaticSource {
13
14
  from: string;
@@ -34,17 +34,28 @@ function packageDirectory(name, root) {
34
34
  throw new Error(`[patchwork] can't find the package "${name}" — a static source has to be a dependency of the site`);
35
35
  }
36
36
  }
37
+ /** A package's static tree: the directory it declares, or its root. */
38
+ function staticDirectory(name, directory) {
39
+ const declared = JSON.parse(readFileSync(join(directory, "package.json"), "utf8")).patchwork?.static;
40
+ if (!declared)
41
+ return directory;
42
+ const path = join(directory, declared);
43
+ if (!existsSync(path)) {
44
+ throw new Error(`[patchwork] ${name} declares "patchwork": {"static": ${JSON.stringify(declared)}}, but ${path} doesn't exist. ` +
45
+ `The field is a path inside the installed package — a package that publishes its static tree as the tarball's own root shouldn't set it at all.`);
46
+ }
47
+ return path;
48
+ }
37
49
  export function resolveStatic(options, root) {
38
50
  return (options.static ?? []).map((source) => {
39
51
  const entry = typeof source === "string" ? { from: source } : source;
40
52
  const isPath = entry.from.startsWith(".") || isAbsolute(entry.from);
41
53
  const directory = isPath ? undefined : packageDirectory(entry.from, root);
42
54
  const path = directory
43
- ? join(directory, JSON.parse(readFileSync(join(directory, "package.json"), "utf8"))
44
- .patchwork?.static ?? ".")
55
+ ? staticDirectory(entry.from, directory)
45
56
  : resolve(root, entry.from);
46
57
  if (!existsSync(path)) {
47
- throw new Error(`[patchwork] static source not found: ${entry.from}`);
58
+ throw new Error(`[patchwork] static source not found: ${entry.from} (${path})`);
48
59
  }
49
60
  const file = statSync(path).isFile();
50
61
  const to = entry.to ?? "/";
@@ -112,6 +123,7 @@ export function staticPlugin(options = {}) {
112
123
  let root;
113
124
  let outDir;
114
125
  let base = "/";
126
+ let serve = false;
115
127
  let logger;
116
128
  return {
117
129
  name: "@patchwork/static",
@@ -120,9 +132,13 @@ export function staticPlugin(options = {}) {
120
132
  root = config.root;
121
133
  outDir = resolve(config.root, config.build.outDir);
122
134
  base = config.base;
135
+ serve = config.command === "serve";
123
136
  logger = config.logger;
124
137
  },
125
138
  async closeBundle() {
139
+ // also called when a dev server shuts down, which has nothing to copy
140
+ if (serve)
141
+ return;
126
142
  for (const source of sources) {
127
143
  const paths = source.file ? [""] : await files(source.path);
128
144
  const kept = [];
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "git+https://github.com/inkandswitch/patchwork-system.git",
6
6
  "directory": "core/patchwork"
7
7
  },
8
- "version": "0.7.0",
8
+ "version": "0.7.2",
9
9
  "author": "Ink & Switch",
10
10
  "type": "module",
11
11
  "license": "MIT",
@@ -44,10 +44,10 @@
44
44
  "sharp": "^0.35.3",
45
45
  "vite-plugin-wasm": "^3.6.0",
46
46
  "@inkandswitch/patchwork-bootloader": "^0.6.2",
47
+ "@inkandswitch/patchwork-elements": "^6.0.0",
47
48
  "@inkandswitch/patchwork-filesystem": "^0.2.5",
48
- "@inkandswitch/patchwork-plugins": "^1.2.0",
49
49
  "@inkandswitch/patchwork-providers": "^0.5.0",
50
- "@inkandswitch/patchwork-elements": "^6.0.0"
50
+ "@inkandswitch/patchwork-plugins": "^1.2.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "rollup": "^4.61.1",
@@ -49,13 +49,16 @@ export function buildInfoPlugin(
49
49
  if (!options.buildInfo) return null;
50
50
  let root: string;
51
51
  let outDir: string;
52
+ let serve = false;
52
53
  return {
53
54
  name: "@patchwork/build-info",
54
55
  configResolved(config) {
55
56
  root = config.root;
56
57
  outDir = join(config.root, config.build.outDir);
58
+ serve = config.command === "serve";
57
59
  },
58
60
  async closeBundle() {
61
+ if (serve) return;
59
62
  const sources = resolveStatic(options, root).map((source) =>
60
63
  source.packageDirectory
61
64
  ? { from: source.from, ...describe(source.packageDirectory) }
@@ -23,6 +23,10 @@ const stylesheets: Record<string, string> = {
23
23
  ),
24
24
  };
25
25
 
26
+ const builtinPaths = new Map(
27
+ Object.entries(builtins).map(([id, fileName]) => [fileName, id])
28
+ );
29
+
26
30
  /**
27
31
  * Workers are `type: "module"` scripts the browser fetches directly, so import
28
32
  * maps don't apply to them and their heavy imports have to resolve to real
@@ -117,6 +121,21 @@ export function devPlugin(options: PatchworkVitePluginOptions = {}): Plugin {
117
121
  return;
118
122
  }
119
123
 
124
+ // A worker script is fetched by URL, and import maps don't apply to
125
+ // those, so code that starts one reaches for the /packages/… path the
126
+ // build emits. In dev those are the optimized deps the page's import
127
+ // map points at — same module, one URL over.
128
+ const builtin = builtinPaths.get(pathname);
129
+ if (builtin) {
130
+ response.statusCode = 302;
131
+ response.setHeader(
132
+ "Location",
133
+ `/@id/${encodeURI(devDependencyId(builtin))}`
134
+ );
135
+ response.end();
136
+ return;
137
+ }
138
+
120
139
  const binary = wasm.get(pathname);
121
140
  if (binary) {
122
141
  try {
@@ -22,7 +22,8 @@ import type { PatchworkVitePluginOptions } from "./patchwork-plugin.js";
22
22
  * `from` is either a package specifier or a path relative to the site root. A
23
23
  * package says where its static tree lives with a `"patchwork": {"static":
24
24
  * "static-dist"}` field in its own package.json; without one, the whole
25
- * package directory is mounted.
25
+ * package directory is mounted — which is what a package that publishes its
26
+ * static tree as the root of its own tarball wants.
26
27
  */
27
28
  export interface PatchworkStaticSource {
28
29
  from: string;
@@ -87,6 +88,22 @@ function packageDirectory(name: string, root: string) {
87
88
  }
88
89
  }
89
90
 
91
+ /** A package's static tree: the directory it declares, or its root. */
92
+ function staticDirectory(name: string, directory: string) {
93
+ const declared = JSON.parse(
94
+ readFileSync(join(directory, "package.json"), "utf8")
95
+ ).patchwork?.static;
96
+ if (!declared) return directory;
97
+ const path = join(directory, declared);
98
+ if (!existsSync(path)) {
99
+ throw new Error(
100
+ `[patchwork] ${name} declares "patchwork": {"static": ${JSON.stringify(declared)}}, but ${path} doesn't exist. ` +
101
+ `The field is a path inside the installed package — a package that publishes its static tree as the tarball's own root shouldn't set it at all.`
102
+ );
103
+ }
104
+ return path;
105
+ }
106
+
90
107
  export function resolveStatic(
91
108
  options: PatchworkVitePluginOptions,
92
109
  root: string
@@ -96,14 +113,12 @@ export function resolveStatic(
96
113
  const isPath = entry.from.startsWith(".") || isAbsolute(entry.from);
97
114
  const directory = isPath ? undefined : packageDirectory(entry.from, root);
98
115
  const path = directory
99
- ? join(
100
- directory,
101
- JSON.parse(readFileSync(join(directory, "package.json"), "utf8"))
102
- .patchwork?.static ?? "."
103
- )
116
+ ? staticDirectory(entry.from, directory)
104
117
  : resolve(root, entry.from);
105
118
  if (!existsSync(path)) {
106
- throw new Error(`[patchwork] static source not found: ${entry.from}`);
119
+ throw new Error(
120
+ `[patchwork] static source not found: ${entry.from} (${path})`
121
+ );
107
122
  }
108
123
  const file = statSync(path).isFile();
109
124
  const to = entry.to ?? "/";
@@ -170,6 +185,7 @@ export function staticPlugin(
170
185
  let root: string;
171
186
  let outDir: string;
172
187
  let base = "/";
188
+ let serve = false;
173
189
  let logger: Logger;
174
190
  return {
175
191
  name: "@patchwork/static",
@@ -178,9 +194,12 @@ export function staticPlugin(
178
194
  root = config.root;
179
195
  outDir = resolve(config.root, config.build.outDir);
180
196
  base = config.base;
197
+ serve = config.command === "serve";
181
198
  logger = config.logger;
182
199
  },
183
200
  async closeBundle() {
201
+ // also called when a dev server shuts down, which has nothing to copy
202
+ if (serve) return;
184
203
  for (const source of sources) {
185
204
  const paths = source.file ? [""] : await files(source.path);
186
205
  const kept: string[] = [];