@markii/pack-build 0.17.0-next.4 → 0.17.0-next.6
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/README.md +59 -0
- package/dist/build-pack-archive.d.ts +34 -0
- package/dist/build-pack-archive.js +98 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -3
- package/dist/pack-build.d.ts +0 -5
- package/dist/pack-export-archive.js +4 -1
- package/dist/pack-export.d.ts +2 -2
- package/dist/pack-export.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,4 +7,63 @@ esbuild's WebAssembly build, writes the prebuilt form, or produces a
|
|
|
7
7
|
toolchain, for authoring hosts and pack build scripts; an app that only
|
|
8
8
|
installs prebuilt packs does not need it. Node only.
|
|
9
9
|
|
|
10
|
+
## Entry points
|
|
11
|
+
|
|
12
|
+
- `buildPackArchive(packFolder, options?)` takes a folder that holds one
|
|
13
|
+
pack and returns the bytes of its `.mkp` archive. It discovers the pack,
|
|
14
|
+
compiles its React views and, when the pack has terminal views, its
|
|
15
|
+
terminal script, and zips the result. The optional `cacheDir` is a folder
|
|
16
|
+
you own for the compiled scripts. It defaults to a folder under the
|
|
17
|
+
system's temporary directory and is never the pack's own folder.
|
|
18
|
+
- `buildPackRegistrationScript(pack, cacheDir, options?)` compiles the
|
|
19
|
+
pack's React sources into one registration script, `webview.js`, and an
|
|
20
|
+
optional stylesheet. It caches the result under `cacheDir`.
|
|
21
|
+
- `buildPackTerminalScript(pack, cacheDir, options?)` does the same for
|
|
22
|
+
the pack's `ansi` sources and produces `terminal.js`. It reports
|
|
23
|
+
`skipped` when no component names an `ansi` source.
|
|
24
|
+
- `exportPackArchive({ pack, cacheDir, build, buildTerminal?, fs })`
|
|
25
|
+
builds through those functions and returns the bytes of a
|
|
26
|
+
`<name>-<version>.mkp` archive. It writes nothing itself.
|
|
27
|
+
- `exportPack` writes the prebuilt form into a destination folder instead
|
|
28
|
+
of an archive.
|
|
29
|
+
|
|
30
|
+
Every build function returns an outcome and never throws. The outcome is
|
|
31
|
+
`built`, `failed` with a reason, or `skipped`. `buildPackArchive` returns
|
|
32
|
+
`built` or `failed`.
|
|
33
|
+
|
|
34
|
+
The two script builders return paths into the cache. Those paths are valid
|
|
35
|
+
until the next build replaces them, so read a script before you build
|
|
36
|
+
again. `buildPackArchive` does this for you and returns bytes.
|
|
37
|
+
|
|
38
|
+
## The pack source
|
|
39
|
+
|
|
40
|
+
The lower-level functions take a pack as `discoverPacks` from
|
|
41
|
+
`@markii/host/internal` returns it. A discovered pack carries its `folder`,
|
|
42
|
+
its parsed `manifest`, the `manifestText`, the absolute `componentPaths` of
|
|
43
|
+
its React sources, `componentPathsByEngine` for other engines, and its
|
|
44
|
+
`scriptsDir` for shared Lua. A build script that only wants an archive
|
|
45
|
+
does not need any of this: it calls `buildPackArchive` with the folder.
|
|
46
|
+
|
|
47
|
+
## Example
|
|
48
|
+
|
|
49
|
+
This script writes one `.mkp` archive for each pack folder under
|
|
50
|
+
`./packs`.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { readdir, writeFile } from 'node:fs/promises';
|
|
54
|
+
import { join } from 'node:path';
|
|
55
|
+
import { buildPackArchive } from '@markii/pack-build';
|
|
56
|
+
|
|
57
|
+
const root = './packs';
|
|
58
|
+
const cacheDir = join(process.cwd(), '.pack-cache');
|
|
59
|
+
|
|
60
|
+
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
61
|
+
if (!entry.isDirectory()) continue;
|
|
62
|
+
const result = await buildPackArchive(join(root, entry.name), { cacheDir });
|
|
63
|
+
if (result.kind === 'failed') throw new Error(result.reason);
|
|
64
|
+
for (const warning of result.warnings) console.warn(warning);
|
|
65
|
+
await writeFile(result.fileName, result.bytes);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
10
69
|
See `docs/packs.md` in the repository for the pack contract.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface BuildPackArchiveOptions {
|
|
2
|
+
/**
|
|
3
|
+
* A folder this caller owns, where compiled scripts are cached. It is
|
|
4
|
+
* never the pack's own folder. Defaults to `markii-pack-build` under the
|
|
5
|
+
* operating system's temporary folder.
|
|
6
|
+
*/
|
|
7
|
+
readonly cacheDir?: string;
|
|
8
|
+
}
|
|
9
|
+
export type BuildPackArchiveOutcome = {
|
|
10
|
+
readonly kind: 'built';
|
|
11
|
+
/** The `.mkp` archive's bytes. */
|
|
12
|
+
readonly bytes: Uint8Array;
|
|
13
|
+
/** `<name>-<version>.mkp`, or `<name>.mkp` when the manifest declares no version. */
|
|
14
|
+
readonly fileName: string;
|
|
15
|
+
/** The build's pack-CSS lint warnings. Empty when there are none. */
|
|
16
|
+
readonly warnings: readonly string[];
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'failed';
|
|
19
|
+
readonly reason: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Builds the `.mkp` archive for the one pack in `packFolder`. The folder
|
|
23
|
+
* either holds a `pack.json` itself or holds exactly one child folder that
|
|
24
|
+
* does. Zero packs, several packs and an invalid pack are all `failed`,
|
|
25
|
+
* with the reason discovery gave. Never throws, and never writes into
|
|
26
|
+
* `packFolder`: the build is cached under `options.cacheDir`.
|
|
27
|
+
*
|
|
28
|
+
* The compiled scripts live in that cache until the next build.
|
|
29
|
+
* `buildPackRegistrationScript` and `buildPackTerminalScript` return paths
|
|
30
|
+
* into it, and those paths are valid only until the next build replaces
|
|
31
|
+
* them. This function reads them back before it returns, so its result
|
|
32
|
+
* holds bytes, not paths.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildPackArchive(packFolder: string, options?: BuildPackArchiveOptions): Promise<BuildPackArchiveOutcome>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `buildPackArchive`: one call from a pack folder to the bytes of its
|
|
3
|
+
* `.mkp` archive. It discovers the pack in the folder, compiles its React
|
|
4
|
+
* views and, when the pack has terminal views, its terminal script, and
|
|
5
|
+
* zips the result. A build script needs this and nothing else from
|
|
6
|
+
* `@markii/host`.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { discoverPacks } from '@markii/host/internal';
|
|
12
|
+
import { buildPackRegistrationScript, buildPackTerminalScript, } from './pack-build.js';
|
|
13
|
+
import { exportPackArchive } from './pack-export-archive.js';
|
|
14
|
+
async function readText(absolutePath) {
|
|
15
|
+
try {
|
|
16
|
+
return await readFile(absolutePath, 'utf8');
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function listEntries(absoluteDir) {
|
|
23
|
+
try {
|
|
24
|
+
const entries = await readdir(absoluteDir, { withFileTypes: true });
|
|
25
|
+
return entries.map((entry) => ({
|
|
26
|
+
name: entry.name,
|
|
27
|
+
isDirectory: entry.isDirectory(),
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function describeThrown(error) {
|
|
35
|
+
return error instanceof Error ? error.message : String(error);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds the `.mkp` archive for the one pack in `packFolder`. The folder
|
|
39
|
+
* either holds a `pack.json` itself or holds exactly one child folder that
|
|
40
|
+
* does. Zero packs, several packs and an invalid pack are all `failed`,
|
|
41
|
+
* with the reason discovery gave. Never throws, and never writes into
|
|
42
|
+
* `packFolder`: the build is cached under `options.cacheDir`.
|
|
43
|
+
*
|
|
44
|
+
* The compiled scripts live in that cache until the next build.
|
|
45
|
+
* `buildPackRegistrationScript` and `buildPackTerminalScript` return paths
|
|
46
|
+
* into it, and those paths are valid only until the next build replaces
|
|
47
|
+
* them. This function reads them back before it returns, so its result
|
|
48
|
+
* holds bytes, not paths.
|
|
49
|
+
*/
|
|
50
|
+
export async function buildPackArchive(packFolder, options = {}) {
|
|
51
|
+
try {
|
|
52
|
+
const cacheDir = options.cacheDir ?? path.join(tmpdir(), 'markii-pack-build');
|
|
53
|
+
const discovered = await discoverPacks([packFolder], readText, listEntries);
|
|
54
|
+
if (discovered.packs.length === 0) {
|
|
55
|
+
const reasons = discovered.skipped.map((entry) => entry.reason);
|
|
56
|
+
return {
|
|
57
|
+
kind: 'failed',
|
|
58
|
+
reason: reasons.length > 0
|
|
59
|
+
? `no pack could be built from "${packFolder}": ${reasons.join('; ')}`
|
|
60
|
+
: `no pack was found in "${packFolder}"`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (discovered.packs.length > 1) {
|
|
64
|
+
return {
|
|
65
|
+
kind: 'failed',
|
|
66
|
+
reason: `"${packFolder}" holds ${String(discovered.packs.length)} packs, and a build takes one`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const pack = discovered.packs[0];
|
|
70
|
+
const built = await exportPackArchive({
|
|
71
|
+
pack,
|
|
72
|
+
cacheDir,
|
|
73
|
+
build: buildPackRegistrationScript,
|
|
74
|
+
buildTerminal: buildPackTerminalScript,
|
|
75
|
+
fs: { readFile: readText, listDirectory: listFileNames },
|
|
76
|
+
});
|
|
77
|
+
if (built.kind === 'failed') {
|
|
78
|
+
return { kind: 'failed', reason: built.reason };
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
kind: 'built',
|
|
82
|
+
bytes: built.bytes,
|
|
83
|
+
fileName: built.fileName,
|
|
84
|
+
warnings: built.warnings,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return { kind: 'failed', reason: describeThrown(error) };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function listFileNames(absoluteDir) {
|
|
92
|
+
try {
|
|
93
|
+
return await readdir(absoluteDir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export type { PackBuildOptions, PackBuildOutcome, PackBuildSource, PackFileReader, } from './pack-build.js';
|
|
2
|
-
export { buildPackRegistrationScript, buildPackTerminalScript,
|
|
3
|
-
export {
|
|
2
|
+
export { buildPackRegistrationScript, buildPackTerminalScript, } from './pack-build.js';
|
|
3
|
+
export type { BuildPackArchiveOptions, BuildPackArchiveOutcome, } from './build-pack-archive.js';
|
|
4
|
+
export { buildPackArchive } from './build-pack-archive.js';
|
|
4
5
|
export type { ConfirmPackOverwrite, ExportPackOptions, PackExportBuilder, PackExportFs, PackExportOutcome, } from './pack-export.js';
|
|
5
|
-
export { exportPack
|
|
6
|
+
export { exportPack } from './pack-export.js';
|
|
6
7
|
export type { ExportPackArchiveOptions, PackExportArchiveOutcome, } from './pack-export-archive.js';
|
|
7
8
|
export { exportPackArchive } from './pack-export-archive.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// prebuilt form. Node only (esbuild-wasm, node:fs, node:crypto). The
|
|
3
3
|
// neutral half of the prebuilt-pack convention (file names, shadowing
|
|
4
4
|
// detection) stays in @markii/host, which this package imports.
|
|
5
|
-
export { buildPackRegistrationScript, buildPackTerminalScript,
|
|
6
|
-
export {
|
|
7
|
-
export { exportPack
|
|
5
|
+
export { buildPackRegistrationScript, buildPackTerminalScript, } from './pack-build.js';
|
|
6
|
+
export { buildPackArchive } from './build-pack-archive.js';
|
|
7
|
+
export { exportPack } from './pack-export.js';
|
|
8
8
|
export { exportPackArchive } from './pack-export-archive.js';
|
package/dist/pack-build.d.ts
CHANGED
|
@@ -21,8 +21,6 @@ export interface PackBuildSource {
|
|
|
21
21
|
}
|
|
22
22
|
/** Reads a file's UTF-8 text, or resolves `undefined` if it does not exist / cannot be read. Never rejects for an ordinary "not found" — injected so this module needs no real filesystem to test. Structurally identical to `apps/vscode`'s own `PackFileReader` (`./discover.ts`), deliberately not shared by import: a host's discovery module and this builder are independent seams. */
|
|
23
23
|
export type PackFileReader = (absolutePath: string) => Promise<string | undefined>;
|
|
24
|
-
/** esbuild-wasm's browser (in-process WebAssembly) `build` function signature — imported as a TYPE only, so this line never causes `esbuild-wasm` itself to be pulled into whatever bundles this module (see `loadEsbuildWasm`'s doc comment for why the VALUE side must never be a static import). */
|
|
25
|
-
type EsbuildBuildFn = typeof import('esbuild-wasm/lib/browser.js').build;
|
|
26
24
|
/**
|
|
27
25
|
* The PRE-build cache key for one pack: a SHA-256 of `BUILDER_VERSION`, the
|
|
28
26
|
* manifest (stable-stringified: `Object.keys` order for a plain
|
|
@@ -78,8 +76,6 @@ export interface PackBuildOptions {
|
|
|
78
76
|
readonly esbuildWasmBinaryPath?: string;
|
|
79
77
|
/** Reads one component source file's UTF-8 text, or `undefined` if unreadable — defaults to real `node:fs`. Injected so a build-failure ("missing component source") path is testable without touching disk. */
|
|
80
78
|
readonly readComponentSource?: PackFileReader;
|
|
81
|
-
/** Replaces esbuild-wasm's `build` function outright — for tests that want to assert cache behavior (or a build failure) without paying for a real esbuild-wasm invocation, and for the ANY-real-invocation counting a cache-hit test needs. Bypasses `loadEsbuildWasm`/the module-path options entirely when given. */
|
|
82
|
-
readonly build?: EsbuildBuildFn;
|
|
83
79
|
}
|
|
84
80
|
/**
|
|
85
81
|
* Builds (or reuses a cached build of) one pack's webview registration
|
|
@@ -112,4 +108,3 @@ export declare function buildPackRegistrationScript(pack: PackBuildSource, cache
|
|
|
112
108
|
* import in a terminal source is compiled away and ignored. Never throws.
|
|
113
109
|
*/
|
|
114
110
|
export declare function buildPackTerminalScript(pack: PackBuildSource, cacheDir: string, options?: PackBuildOptions): Promise<PackBuildOutcome>;
|
|
115
|
-
export {};
|
|
@@ -183,7 +183,10 @@ export async function exportPackArchive(options) {
|
|
|
183
183
|
for (const file of luaFiles) {
|
|
184
184
|
zipInput[`scripts/${file.name}`] = encoder.encode(file.text);
|
|
185
185
|
}
|
|
186
|
-
|
|
186
|
+
// A fixed modification time, so the same pack zips to the same bytes at
|
|
187
|
+
// any hour and on any machine. The date is built from local components,
|
|
188
|
+
// so the stored DOS time reads 1980-01-01 00:00 in every time zone.
|
|
189
|
+
const bytes = zipSync(zipInput, { mtime: new Date(1980, 0, 1, 0, 0, 0) });
|
|
187
190
|
return {
|
|
188
191
|
kind: 'built',
|
|
189
192
|
packName,
|
package/dist/pack-export.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type PackExportOutcome = {
|
|
|
58
58
|
/** The build's pack-CSS lint warnings, passed through unchanged. */
|
|
59
59
|
readonly warnings: readonly string[];
|
|
60
60
|
} | {
|
|
61
|
-
readonly kind: '
|
|
61
|
+
readonly kind: 'declined';
|
|
62
62
|
readonly packName: string;
|
|
63
63
|
} | {
|
|
64
64
|
readonly kind: 'failed';
|
|
@@ -80,7 +80,7 @@ export declare function resolveExportTarget(root: string, ...segments: readonly
|
|
|
80
80
|
* NEVER throws: every failure (an unsafe `exportName`, a build failure, an
|
|
81
81
|
* unreadable build output or `pack.json`, a write that fails) comes back as
|
|
82
82
|
* `{ kind: 'failed', reason }`. A declined overwrite comes back as
|
|
83
|
-
* `{ kind: '
|
|
83
|
+
* `{ kind: 'declined' }` with nothing written.
|
|
84
84
|
*
|
|
85
85
|
* Order of operations: resolve the destination folder and every write
|
|
86
86
|
* target inside it (path-jailed against `destinationDir`, including the
|
package/dist/pack-export.js
CHANGED
|
@@ -67,7 +67,7 @@ function packNameOf(pack) {
|
|
|
67
67
|
* NEVER throws: every failure (an unsafe `exportName`, a build failure, an
|
|
68
68
|
* unreadable build output or `pack.json`, a write that fails) comes back as
|
|
69
69
|
* `{ kind: 'failed', reason }`. A declined overwrite comes back as
|
|
70
|
-
* `{ kind: '
|
|
70
|
+
* `{ kind: 'declined' }` with nothing written.
|
|
71
71
|
*
|
|
72
72
|
* Order of operations: resolve the destination folder and every write
|
|
73
73
|
* target inside it (path-jailed against `destinationDir`, including the
|
|
@@ -274,7 +274,7 @@ export async function exportPack(options) {
|
|
|
274
274
|
return { kind: 'failed', packName, reason: describeThrown(err) };
|
|
275
275
|
}
|
|
276
276
|
if (!proceed) {
|
|
277
|
-
return { kind: '
|
|
277
|
+
return { kind: 'declined', packName };
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/pack-build",
|
|
3
|
-
"version": "0.17.0-next.
|
|
3
|
+
"version": "0.17.0-next.6",
|
|
4
4
|
"description": "Pack compilation for Markii authoring hosts: compiles a pack's component sources (and imported CSS) with esbuild-wasm's in-process path, and writes the prebuilt form or a .mkp archive. Node only.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build:dist": "rm -rf dist && tsc -p tsconfig.build.json"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@markii/host": "^0.17.0-next.
|
|
43
|
-
"@markii/pack": "^0.17.0-next.
|
|
42
|
+
"@markii/host": "^0.17.0-next.6",
|
|
43
|
+
"@markii/pack": "^0.17.0-next.6",
|
|
44
44
|
"esbuild-wasm": "^0.28.2",
|
|
45
45
|
"fflate": "^0.8.3"
|
|
46
46
|
},
|