gutterpress 0.11.10-alpha.2 → 0.11.10-alpha.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 +24 -46
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +10 -6
- package/dist/{build-dfpvvhys.js → build-npxq1tvm.js} +7 -6
- package/dist/cdp-jg95s674.js +15 -0
- package/dist/cdp-smdx6mr3.js +16 -0
- package/dist/{cli-d8rdk1y7.js → cli-30j93gzt.js} +11 -335
- package/dist/{cli-pfczdbyd.js → cli-5qr3z0qa.js} +10 -404
- package/dist/{cli-c41yr7he.js → cli-7fkhgtfn.js} +3 -1
- package/dist/{cli-k2hwrp7s.js → cli-97bdz5k0.js} +1 -4
- package/dist/cli-kkzka31p.js +261 -0
- package/dist/{cli-p679pvce.js → cli-vbzkkcbv.js} +2 -2
- package/dist/{cli-4xksazfr.js → cli-z8ye91f0.js} +283 -1142
- package/dist/cli.js +12 -14
- package/dist/{doctor-hheax5yc.js → doctor-ykr6gtpv.js} +3 -3
- package/dist/engine/shared/cdp.d.ts +23 -19
- package/dist/engine-3gwctsty.js +24 -0
- package/dist/engine-mwshptgf.js +25 -0
- package/dist/{ext-q5024j6v.js → ext-bna9nr3v.js} +7 -6
- package/dist/{index-1a4rx87v.js → index-322fxk25.js} +344 -1130
- package/dist/{index-kqftehgy.js → index-dzp0hc2c.js} +4 -2
- package/dist/{index-fnhak0nh.js → index-qf7y4d85.js} +10 -404
- package/dist/index-r4wvy5v3.js +267 -0
- package/dist/{index-t43k5rq1.js → index-rzm1phg0.js} +23 -356
- package/dist/index.d.ts +0 -1
- package/dist/index.js +12 -87
- package/dist/lib/build-error.d.ts +4 -5
- package/dist/lib/build-preflight.d.ts +1 -35
- package/dist/lib/build-runner.d.ts +27 -44
- package/dist/lib/chromium.d.ts +5 -1
- package/dist/lib/engine.d.ts +3 -18
- package/dist/lib/extension-manager.d.ts +2 -2
- package/dist/lib/extension-manifest.d.ts +1 -1
- package/dist/lib/markdown/assemble.d.ts +2 -9
- package/dist/lib/markdown/index.d.ts +2 -4
- package/dist/lib/markdown/plugins.d.ts +0 -3
- package/dist/lib/npm-plugin-installer.d.ts +6 -1
- package/dist/lib/plugin-vendor.d.ts +10 -78
- package/dist/lib/source-provider.d.ts +1 -1
- package/dist/lib/validation-exec.d.ts +5 -6
- package/dist/{new-6xm4tdxb.js → new-0hv3nwqj.js} +9 -8
- package/dist/{preflight-ezt8rvg3.js → preflight-pk0k25fm.js} +7 -6
- package/dist/preview/file-watcher.d.ts +4 -43
- package/dist/preview/http-server.d.ts +1 -3
- package/dist/{preview-hehs46dd.js → preview-8b1fn59h.js} +7 -6
- package/dist/{preview-shell-gqnc5xxe.js → preview-shell-tqndvhbx.js} +5 -7
- package/dist/{publish-ha505ejq.js → publish-ba5h76tz.js} +7 -6
- package/dist/render.d.ts +1 -1
- package/dist/render.js +1 -9
- package/dist/{source-provider-ta09wegv.js → source-provider-pzmz0br9.js} +1 -1
- package/dist/{source-provider-7c2ww3bz.js → source-provider-qbthnmqw.js} +1 -1
- package/dist/{validate-je56vdbj.js → validate-4rq2r5b3.js} +7 -6
- package/package.json +1 -4
- package/dist/audit-v9594a72.js +0 -69
- package/dist/engine-qtfrfbpk.js +0 -41
- package/dist/engine-trte2tg2.js +0 -40
- package/dist/lib/browser-pool.d.ts +0 -35
- package/dist/lib/lint-runner.d.ts +0 -29
- package/dist/lint-tbywt66a.js +0 -68
|
@@ -13,22 +13,20 @@ import {
|
|
|
13
13
|
stampCreator,
|
|
14
14
|
stripAnnotations,
|
|
15
15
|
warn
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-vbzkkcbv.js";
|
|
17
17
|
import {
|
|
18
18
|
DEBOUNCE,
|
|
19
19
|
UsageError,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-97bdz5k0.js";
|
|
22
22
|
import {
|
|
23
23
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
24
|
-
RENDER_TIMEOUT_MS,
|
|
25
|
-
REQUIRED_MILESTONE,
|
|
26
|
-
closeBrowser,
|
|
27
24
|
getAssetPath,
|
|
28
|
-
getAssetsDir
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
getAssetsDir
|
|
26
|
+
} from "./cli-30j93gzt.js";
|
|
27
|
+
import {
|
|
28
|
+
BuildError
|
|
29
|
+
} from "./cli-46ycxe6r.js";
|
|
32
30
|
import {
|
|
33
31
|
INSTALL_HINTS,
|
|
34
32
|
execCapture,
|
|
@@ -36,32 +34,24 @@ import {
|
|
|
36
34
|
requireChromiumExecutable,
|
|
37
35
|
resolveChromiumExecutable,
|
|
38
36
|
spawnCapture
|
|
39
|
-
} from "./cli-
|
|
40
|
-
import {
|
|
41
|
-
BuildError
|
|
42
|
-
} from "./cli-46ycxe6r.js";
|
|
37
|
+
} from "./cli-7fkhgtfn.js";
|
|
43
38
|
import {
|
|
44
39
|
PLUGINS_DIR,
|
|
45
40
|
VENDORED_NPM_DIR,
|
|
46
|
-
VENDOR_RECEIPT_FILE,
|
|
47
|
-
VENDOR_RECEIPT_VERSION,
|
|
48
41
|
assertWindowsSafeRelativePath,
|
|
49
|
-
computeVendorTreeDigest,
|
|
50
42
|
hasUncommittedChanges,
|
|
51
43
|
isExactNpmVersion,
|
|
52
44
|
isValidNpmPackageName,
|
|
53
45
|
listWorkdirChanges,
|
|
54
|
-
npmRegistryUrl,
|
|
55
|
-
packageEntryFormat,
|
|
56
46
|
packageResolutionTargets,
|
|
57
47
|
parseNpmPluginSpec,
|
|
58
48
|
resolvePackageEntry,
|
|
59
49
|
resolveVendoredPluginInstallRoot,
|
|
50
|
+
safePackageTarget,
|
|
60
51
|
vendoredNpmPluginPackageDir,
|
|
61
52
|
vendoredNpmPluginRoot,
|
|
62
|
-
verifyVendoredPlugin,
|
|
63
53
|
windowsPathKey
|
|
64
|
-
} from "./cli-
|
|
54
|
+
} from "./cli-5qr3z0qa.js";
|
|
65
55
|
import {
|
|
66
56
|
detectProjectSource,
|
|
67
57
|
repoSubPath
|
|
@@ -2797,7 +2787,7 @@ async function inlineShapeUrls(html, baseDir) {
|
|
|
2797
2787
|
}
|
|
2798
2788
|
|
|
2799
2789
|
// src/checks/source/local-ref-parser.ts
|
|
2800
|
-
import { readFile as
|
|
2790
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
2801
2791
|
|
|
2802
2792
|
// src/lib/markdown/renderer.ts
|
|
2803
2793
|
import MarkdownIt from "markdown-it";
|
|
@@ -4616,16 +4606,11 @@ function layerSlug(name) {
|
|
|
4616
4606
|
}
|
|
4617
4607
|
|
|
4618
4608
|
// src/lib/markdown/plugins.ts
|
|
4619
|
-
import {
|
|
4620
|
-
import {
|
|
4621
|
-
import {
|
|
4622
|
-
import {
|
|
4623
|
-
import {
|
|
4624
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4625
|
-
import Module, { builtinModules, createRequire } from "node:module";
|
|
4626
|
-
import { parse as parseJavaScript } from "acorn";
|
|
4627
|
-
import { parse as parseModuleImports } from "es-module-lexer/js";
|
|
4628
|
-
import { imports as resolvePackageImports } from "resolve.exports";
|
|
4609
|
+
import { existsSync as existsSync6, readFileSync, statSync as statSync2, unlinkSync } from "node:fs";
|
|
4610
|
+
import { link, unlink } from "node:fs/promises";
|
|
4611
|
+
import { isBuiltin } from "node:module";
|
|
4612
|
+
import { resolve as resolve4, join, dirname as dirname2, basename, extname, isAbsolute, relative, sep } from "node:path";
|
|
4613
|
+
import { pathToFileURL } from "node:url";
|
|
4629
4614
|
|
|
4630
4615
|
// src/lib/style-declarations.ts
|
|
4631
4616
|
import { existsSync as existsSync5 } from "node:fs";
|
|
@@ -4737,513 +4722,97 @@ function resolveExtension(dir, meta, subject) {
|
|
|
4737
4722
|
};
|
|
4738
4723
|
}
|
|
4739
4724
|
// src/lib/markdown/plugins.ts
|
|
4740
|
-
var
|
|
4741
|
-
var
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
const parts = request.split("/");
|
|
4759
|
-
if (request.startsWith("@")) {
|
|
4760
|
-
if (parts.length < 2)
|
|
4761
|
-
return null;
|
|
4762
|
-
return { name: `${parts[0]}/${parts[1]}`, subpath: parts.slice(2).join("/") };
|
|
4763
|
-
}
|
|
4764
|
-
return { name: parts[0], subpath: parts.slice(1).join("/") };
|
|
4765
|
-
}
|
|
4766
|
-
function resolveVerifiedTarget(pkg, resolution) {
|
|
4767
|
-
const target = resolution.target.startsWith("./") ? resolution.target.slice(2) : resolution.target;
|
|
4768
|
-
if (!target || target.includes("\\") || target.includes("*") || target.includes("?") || target.includes("#")) {
|
|
4769
|
-
return null;
|
|
4770
|
-
}
|
|
4771
|
-
try {
|
|
4772
|
-
assertWindowsSafeRelativePath(target);
|
|
4773
|
-
} catch {
|
|
4774
|
-
return null;
|
|
4775
|
-
}
|
|
4776
|
-
if (target.normalize("NFKC").split("/").some((part) => part.toLowerCase() === "node_modules")) {
|
|
4777
|
-
return null;
|
|
4778
|
-
}
|
|
4779
|
-
const base = resolve4(pkg.packageDir, ...target.split("/"));
|
|
4780
|
-
if (!pathContains(pkg.packageDir, base))
|
|
4781
|
-
return null;
|
|
4782
|
-
const candidates = resolution.exact ? [base] : [
|
|
4783
|
-
base,
|
|
4784
|
-
`${base}.js`,
|
|
4785
|
-
`${base}.cjs`,
|
|
4786
|
-
`${base}.mjs`,
|
|
4787
|
-
`${base}.json`,
|
|
4788
|
-
join(base, "index.js"),
|
|
4789
|
-
join(base, "index.cjs"),
|
|
4790
|
-
join(base, "index.mjs"),
|
|
4791
|
-
join(base, "index.json")
|
|
4792
|
-
];
|
|
4793
|
-
for (const candidate of candidates) {
|
|
4794
|
-
try {
|
|
4795
|
-
const info2 = lstatSync(candidate);
|
|
4796
|
-
const extension = extname(candidate);
|
|
4797
|
-
if (info2.isFile() && !info2.isSymbolicLink() && (!extension || [".js", ".cjs", ".mjs", ".json"].includes(extension))) {
|
|
4798
|
-
return candidate;
|
|
4799
|
-
}
|
|
4800
|
-
} catch {}
|
|
4801
|
-
}
|
|
4802
|
-
return null;
|
|
4803
|
-
}
|
|
4804
|
-
function resolveVerifiedRequest(pkg, subpath, condition) {
|
|
4805
|
-
for (const resolution of packageResolutionTargets(pkg.manifest, subpath, condition)) {
|
|
4806
|
-
const resolved = resolveVerifiedTarget(pkg, resolution);
|
|
4807
|
-
if (resolved)
|
|
4808
|
-
return resolved;
|
|
4809
|
-
}
|
|
4810
|
-
return null;
|
|
4811
|
-
}
|
|
4812
|
-
function resolveApprovedPackageRequest(owner, request, condition, tree, depth = 0) {
|
|
4813
|
-
if (depth > 8)
|
|
4814
|
-
return null;
|
|
4815
|
-
if (nodeBuiltins.has(request)) {
|
|
4816
|
-
return request.startsWith("node:") ? request : `node:${request}`;
|
|
4817
|
-
}
|
|
4818
|
-
if (request.startsWith("#")) {
|
|
4819
|
-
let targets;
|
|
4725
|
+
var vendoredRootsOpened = new Set;
|
|
4726
|
+
var bunVendoredResolverInstalled = false;
|
|
4727
|
+
function isInsideDir(candidate, root) {
|
|
4728
|
+
return candidate === root || candidate.startsWith(root.endsWith(sep) ? root : root + sep);
|
|
4729
|
+
}
|
|
4730
|
+
function resolveBareSpecifierSync(specifier, fromDir, stopRoot, condition) {
|
|
4731
|
+
const parts = specifier.split("/");
|
|
4732
|
+
const name = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
4733
|
+
const subpath = specifier.slice(name.length).replace(/^\//, "");
|
|
4734
|
+
for (let dir = fromDir;isInsideDir(dir, stopRoot); dir = dirname2(dir)) {
|
|
4735
|
+
const packageDir = join(dir, "node_modules", ...name.split("/"));
|
|
4736
|
+
const packageJson = join(packageDir, "package.json");
|
|
4737
|
+
if (!existsSync6(packageJson)) {
|
|
4738
|
+
if (dir === stopRoot)
|
|
4739
|
+
break;
|
|
4740
|
+
continue;
|
|
4741
|
+
}
|
|
4742
|
+
let manifest;
|
|
4820
4743
|
try {
|
|
4821
|
-
|
|
4822
|
-
require: condition === "require"
|
|
4823
|
-
}) ?? [];
|
|
4744
|
+
manifest = JSON.parse(readFileSync(packageJson, "utf8"));
|
|
4824
4745
|
} catch {
|
|
4825
4746
|
return null;
|
|
4826
4747
|
}
|
|
4827
|
-
for (const
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4748
|
+
for (const target of packageResolutionTargets(manifest, subpath, condition)) {
|
|
4749
|
+
const rel = safePackageTarget(target.target);
|
|
4750
|
+
if (!rel)
|
|
4751
|
+
continue;
|
|
4752
|
+
const base = resolve4(packageDir, ...rel.split("/"));
|
|
4753
|
+
if (!isInsideDir(base, packageDir))
|
|
4754
|
+
continue;
|
|
4755
|
+
const candidates = target.exact ? [base] : [base, `${base}.mjs`, `${base}.js`, `${base}.cjs`, `${base}.json`, join(base, "index.mjs"), join(base, "index.js"), join(base, "index.cjs")];
|
|
4756
|
+
for (const candidate of candidates) {
|
|
4757
|
+
try {
|
|
4758
|
+
if (statSync2(candidate).isFile())
|
|
4759
|
+
return candidate;
|
|
4760
|
+
} catch {}
|
|
4836
4761
|
}
|
|
4837
4762
|
}
|
|
4838
4763
|
return null;
|
|
4839
4764
|
}
|
|
4840
|
-
|
|
4841
|
-
if (!parsed)
|
|
4842
|
-
return null;
|
|
4843
|
-
const targetPath = parsed.name === owner.name ? owner.path : owner.dependencies[parsed.name];
|
|
4844
|
-
const target = targetPath ? tree.byPath.get(targetPath) : undefined;
|
|
4845
|
-
if (!target)
|
|
4846
|
-
return null;
|
|
4847
|
-
if (parsed.subpath)
|
|
4848
|
-
return resolveVerifiedRequest(target, parsed.subpath, condition);
|
|
4849
|
-
return condition === "require" ? target.requireEntryPath ?? null : target.entryPath ?? null;
|
|
4850
|
-
}
|
|
4851
|
-
function unresolvedVendorRequest(request, parent) {
|
|
4852
|
-
const error2 = new Error(`Cannot resolve undeclared or unexported package request "${request}" from ${parent}.`);
|
|
4853
|
-
error2.code = "MODULE_NOT_FOUND";
|
|
4854
|
-
return error2;
|
|
4765
|
+
return null;
|
|
4855
4766
|
}
|
|
4856
|
-
function
|
|
4857
|
-
if (
|
|
4767
|
+
function installBunVendoredResolver() {
|
|
4768
|
+
if (bunVendoredResolverInstalled)
|
|
4858
4769
|
return;
|
|
4859
|
-
|
|
4860
|
-
const
|
|
4861
|
-
|
|
4862
|
-
if (parent?.filename && (request.startsWith("#") || barePackageRequest(request))) {
|
|
4863
|
-
for (const tree of vendorCjsTrees.values()) {
|
|
4864
|
-
const owner = tree.packages.find((pkg) => pathContains(pkg.packageDir, parent.filename));
|
|
4865
|
-
if (!owner)
|
|
4866
|
-
continue;
|
|
4867
|
-
const resolved = resolveApprovedPackageRequest(owner, request, "require", tree);
|
|
4868
|
-
if (resolved)
|
|
4869
|
-
return resolved;
|
|
4870
|
-
if (!nodeBuiltins.has(request)) {
|
|
4871
|
-
throw unresolvedVendorRequest(request, parent.filename);
|
|
4872
|
-
}
|
|
4873
|
-
}
|
|
4874
|
-
}
|
|
4875
|
-
return Reflect.apply(original, this, [request, parent, isMain, options]);
|
|
4876
|
-
};
|
|
4877
|
-
}
|
|
4878
|
-
async function getIsolatedVendorBase() {
|
|
4879
|
-
isolatedVendorBase ??= mkdtemp(join(tmpdir(), "gutterpress-plugin-loads-"));
|
|
4880
|
-
const base = await isolatedVendorBase;
|
|
4881
|
-
isolatedVendorBasePath = base;
|
|
4882
|
-
ensureExitCleanupRegistered();
|
|
4883
|
-
return base;
|
|
4884
|
-
}
|
|
4885
|
-
function isolatedPackageCopyFilter(packageDir, candidate) {
|
|
4886
|
-
const candidateRelative = relative(packageDir, candidate);
|
|
4887
|
-
if (!candidateRelative)
|
|
4888
|
-
return true;
|
|
4889
|
-
const parts = candidateRelative.split(/[\\/]/);
|
|
4890
|
-
const nodeModulesAt = parts.findIndex((part) => part.normalize("NFKC").toLowerCase() === "node_modules");
|
|
4891
|
-
if (nodeModulesAt < 0)
|
|
4892
|
-
return true;
|
|
4893
|
-
if (nodeModulesAt === 0 && parts[0] === "node_modules")
|
|
4894
|
-
return false;
|
|
4895
|
-
throw new Error(`Package contains bundled node_modules content: ${candidateRelative}`);
|
|
4896
|
-
}
|
|
4897
|
-
function rewrittenSpecifier(owner, specifier, condition, tree) {
|
|
4898
|
-
if (!specifier.startsWith("#") && !barePackageRequest(specifier) && !nodeBuiltins.has(specifier)) {
|
|
4899
|
-
return null;
|
|
4900
|
-
}
|
|
4901
|
-
const resolved = resolveApprovedPackageRequest(owner, specifier, condition, tree);
|
|
4902
|
-
if (!resolved) {
|
|
4903
|
-
throw new Error(`Package request "${specifier}" from ${owner.name} is not a receipt-approved dependency.`);
|
|
4904
|
-
}
|
|
4905
|
-
if (resolved.startsWith("node:"))
|
|
4906
|
-
return { replacement: resolved };
|
|
4907
|
-
return {
|
|
4908
|
-
replacement: condition === "require" ? resolved : pathToFileURL(resolved).href,
|
|
4909
|
-
targetPath: resolved
|
|
4910
|
-
};
|
|
4911
|
-
}
|
|
4912
|
-
function localModuleTarget(absolute, specifier, condition) {
|
|
4913
|
-
if (!specifier.startsWith(".") && !specifier.startsWith("file:") && !isAbsolute(specifier)) {
|
|
4914
|
-
return null;
|
|
4915
|
-
}
|
|
4916
|
-
try {
|
|
4917
|
-
if (condition === "require" && !specifier.startsWith("file:")) {
|
|
4918
|
-
return createRequire(absolute).resolve(specifier);
|
|
4919
|
-
}
|
|
4920
|
-
const url = specifier.startsWith("file:") ? new URL(specifier) : isAbsolute(specifier) ? pathToFileURL(specifier) : new URL(specifier, pathToFileURL(absolute));
|
|
4921
|
-
const target = fileURLToPath(url);
|
|
4922
|
-
return lstatSync(target).isFile() ? target : null;
|
|
4923
|
-
} catch {
|
|
4924
|
-
return null;
|
|
4925
|
-
}
|
|
4926
|
-
}
|
|
4927
|
-
function packageOwningFile(tree, file) {
|
|
4928
|
-
return tree.packages.find((pkg) => pathContains(pkg.packageDir, file));
|
|
4929
|
-
}
|
|
4930
|
-
function addReachableTarget(targets, target, specifier, absolute, tree) {
|
|
4931
|
-
if (!target)
|
|
4770
|
+
bunVendoredResolverInstalled = true;
|
|
4771
|
+
const bun = globalThis.Bun;
|
|
4772
|
+
if (typeof bun?.plugin !== "function")
|
|
4932
4773
|
return;
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
visitor(node);
|
|
4940
|
-
for (const [key, value] of Object.entries(node)) {
|
|
4941
|
-
if (["type", "start", "end", "loc", "range"].includes(key) || !value)
|
|
4942
|
-
continue;
|
|
4943
|
-
if (Array.isArray(value)) {
|
|
4944
|
-
for (const item of value) {
|
|
4945
|
-
if (item && typeof item === "object" && typeof item.type === "string") {
|
|
4946
|
-
visitSyntax(item, visitor);
|
|
4774
|
+
bun.plugin({
|
|
4775
|
+
name: "gutterpress-vendored-plugins",
|
|
4776
|
+
setup(build) {
|
|
4777
|
+
build.onResolve({ filter: /^[^./]/ }, (args) => {
|
|
4778
|
+
if (!args.importer || isAbsolute(args.path) || /^[a-z]+:/i.test(args.path) || isBuiltin(args.path)) {
|
|
4779
|
+
return;
|
|
4947
4780
|
}
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
}
|
|
4954
|
-
function memberName(node) {
|
|
4955
|
-
if (node.type !== "MemberExpression")
|
|
4956
|
-
return null;
|
|
4957
|
-
const property = node.property;
|
|
4958
|
-
if (!property)
|
|
4959
|
-
return null;
|
|
4960
|
-
if (property.type === "Identifier")
|
|
4961
|
-
return property.name;
|
|
4962
|
-
if (property.type === "Literal" && typeof property.value === "string")
|
|
4963
|
-
return property.value;
|
|
4964
|
-
return null;
|
|
4965
|
-
}
|
|
4966
|
-
function isReceiptBoundRequireCall(node) {
|
|
4967
|
-
if (node.type !== "CallExpression")
|
|
4968
|
-
return false;
|
|
4969
|
-
const callee = node.callee;
|
|
4970
|
-
if (!callee)
|
|
4971
|
-
return false;
|
|
4972
|
-
if (callee.type === "Identifier" && callee.name === "require")
|
|
4973
|
-
return true;
|
|
4974
|
-
if (callee.type !== "MemberExpression")
|
|
4975
|
-
return false;
|
|
4976
|
-
const object2 = callee.object;
|
|
4977
|
-
const property = memberName(callee);
|
|
4978
|
-
return Boolean(object2 && (object2.type === "Identifier" && object2.name === "module" && property === "require" || object2.type === "Identifier" && object2.name === "require" && property === "resolve"));
|
|
4979
|
-
}
|
|
4980
|
-
function rewriteCommonJsRequires(source, absolute, pkg, tree) {
|
|
4981
|
-
const ast = parseJavaScript(source, {
|
|
4982
|
-
ecmaVersion: "latest",
|
|
4983
|
-
sourceType: "script",
|
|
4984
|
-
allowHashBang: true,
|
|
4985
|
-
allowReturnOutsideFunction: true
|
|
4986
|
-
});
|
|
4987
|
-
const replacements = [];
|
|
4988
|
-
const targets = new Set;
|
|
4989
|
-
visitSyntax(ast, (node) => {
|
|
4990
|
-
if (!isReceiptBoundRequireCall(node))
|
|
4991
|
-
return;
|
|
4992
|
-
const args = node.arguments;
|
|
4993
|
-
const argument = args?.[0];
|
|
4994
|
-
if (!argument || argument.type !== "Literal" || typeof argument.value !== "string") {
|
|
4995
|
-
throw new Error(`Dynamic require in ${pkg.name}/${relative(pkg.packageDir, absolute)} must use a string literal.`);
|
|
4996
|
-
}
|
|
4997
|
-
const rewritten = rewrittenSpecifier(pkg, argument.value, "require", tree);
|
|
4998
|
-
if (rewritten) {
|
|
4999
|
-
replacements.push({
|
|
5000
|
-
start: argument.start,
|
|
5001
|
-
end: argument.end,
|
|
5002
|
-
value: JSON.stringify(rewritten.replacement)
|
|
5003
|
-
});
|
|
5004
|
-
addReachableTarget(targets, rewritten.targetPath, argument.value, absolute, tree);
|
|
5005
|
-
} else {
|
|
5006
|
-
const target = localModuleTarget(absolute, argument.value, "require");
|
|
5007
|
-
if (!target) {
|
|
5008
|
-
throw new Error(`CommonJS module "${argument.value}" from ${pkg.name}/${relative(pkg.packageDir, absolute)} could not be resolved.`);
|
|
5009
|
-
}
|
|
5010
|
-
addReachableTarget(targets, target, argument.value, absolute, tree);
|
|
5011
|
-
}
|
|
5012
|
-
});
|
|
5013
|
-
return { replacements, targets };
|
|
5014
|
-
}
|
|
5015
|
-
async function rewriteReachableModuleImports(entryPath, tree) {
|
|
5016
|
-
const pending = [entryPath];
|
|
5017
|
-
const visited = new Set;
|
|
5018
|
-
while (pending.length > 0) {
|
|
5019
|
-
const absolute = pending.pop();
|
|
5020
|
-
if (visited.has(absolute))
|
|
5021
|
-
continue;
|
|
5022
|
-
visited.add(absolute);
|
|
5023
|
-
const extension = extname(absolute);
|
|
5024
|
-
if (![".js", ".mjs", ".cjs"].includes(extension))
|
|
5025
|
-
continue;
|
|
5026
|
-
const pkg = packageOwningFile(tree, absolute);
|
|
5027
|
-
if (!pkg)
|
|
5028
|
-
throw new Error(`Reachable plugin module is outside the verified package tree: ${absolute}`);
|
|
5029
|
-
const source = await readFile10(absolute, "utf8");
|
|
5030
|
-
const [imports] = parseModuleImports(source, absolute);
|
|
5031
|
-
const replacements = [];
|
|
5032
|
-
const targets = new Set;
|
|
5033
|
-
for (const imported of imports) {
|
|
5034
|
-
if (imported.d === -2)
|
|
5035
|
-
continue;
|
|
5036
|
-
if (imported.n === undefined) {
|
|
5037
|
-
throw new Error(`Dynamic import in ${pkg.name}/${relative(pkg.packageDir, absolute)} must use a string literal.`);
|
|
5038
|
-
}
|
|
5039
|
-
const rewritten2 = rewrittenSpecifier(pkg, imported.n, "import", tree);
|
|
5040
|
-
if (rewritten2) {
|
|
5041
|
-
addReachableTarget(targets, rewritten2.targetPath, imported.n, absolute, tree);
|
|
5042
|
-
if (imported.d === -1) {
|
|
5043
|
-
const quote = source[imported.s - 1];
|
|
5044
|
-
if (quote !== '"' && quote !== "'" || source[imported.e] !== quote) {
|
|
5045
|
-
throw new Error(`Could not safely rewrite an import in ${absolute}.`);
|
|
4781
|
+
let root;
|
|
4782
|
+
for (const opened of vendoredRootsOpened) {
|
|
4783
|
+
if (isInsideDir(args.importer, opened)) {
|
|
4784
|
+
root = opened;
|
|
4785
|
+
break;
|
|
5046
4786
|
}
|
|
5047
|
-
replacements.push({
|
|
5048
|
-
start: imported.s - 1,
|
|
5049
|
-
end: imported.e + 1,
|
|
5050
|
-
value: JSON.stringify(rewritten2.replacement)
|
|
5051
|
-
});
|
|
5052
|
-
} else {
|
|
5053
|
-
replacements.push({
|
|
5054
|
-
start: imported.s,
|
|
5055
|
-
end: imported.e,
|
|
5056
|
-
value: JSON.stringify(rewritten2.replacement)
|
|
5057
|
-
});
|
|
5058
4787
|
}
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
} else if (imported.n.startsWith(".") || imported.n.startsWith("file:") || isAbsolute(imported.n)) {
|
|
5064
|
-
throw new Error(`ESM module "${imported.n}" from ${pkg.name}/${relative(pkg.packageDir, absolute)} could not be resolved.`);
|
|
5065
|
-
}
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
if (extension === ".cjs" || extension === ".js" && pkg.manifest.type !== "module") {
|
|
5069
|
-
const commonJs = rewriteCommonJsRequires(source, absolute, pkg, tree);
|
|
5070
|
-
replacements.push(...commonJs.replacements);
|
|
5071
|
-
for (const target of commonJs.targets)
|
|
5072
|
-
targets.add(target);
|
|
5073
|
-
}
|
|
5074
|
-
replacements.sort((a, b) => b.start - a.start);
|
|
5075
|
-
let rewritten = source;
|
|
5076
|
-
for (const replacement of replacements) {
|
|
5077
|
-
rewritten = rewritten.slice(0, replacement.start) + replacement.value + rewritten.slice(replacement.end);
|
|
5078
|
-
}
|
|
5079
|
-
if (replacements.length > 0)
|
|
5080
|
-
await writeFile(absolute, rewritten, "utf8");
|
|
5081
|
-
pending.push(...targets);
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5084
|
-
function remapPackageEntry(source, destination, entry) {
|
|
5085
|
-
if (!entry)
|
|
5086
|
-
return;
|
|
5087
|
-
const entryRelative = relative(source.packageDir, entry);
|
|
5088
|
-
if (!pathContains(source.packageDir, entry) || !entryRelative) {
|
|
5089
|
-
throw new Error(`Verified entry for ${source.name} is outside its package.`);
|
|
5090
|
-
}
|
|
5091
|
-
return resolve4(destination, entryRelative);
|
|
5092
|
-
}
|
|
5093
|
-
async function createIsolatedVendorTree(verified, address) {
|
|
5094
|
-
const base = await getIsolatedVendorBase();
|
|
5095
|
-
const loadRoot = join(base, address);
|
|
5096
|
-
await mkdir(loadRoot);
|
|
5097
|
-
try {
|
|
5098
|
-
const packageMap = new Map;
|
|
5099
|
-
const sorted = [...verified.packages].sort((a, b) => a.path.localeCompare(b.path));
|
|
5100
|
-
for (let index = 0;index < sorted.length; index++) {
|
|
5101
|
-
const source = sorted[index];
|
|
5102
|
-
const destination = join(loadRoot, "packages", String(index), "package");
|
|
5103
|
-
await mkdir(dirname2(destination), { recursive: true });
|
|
5104
|
-
await cp(source.packageDir, destination, {
|
|
5105
|
-
recursive: true,
|
|
5106
|
-
filter: (candidate) => isolatedPackageCopyFilter(source.packageDir, candidate)
|
|
5107
|
-
});
|
|
5108
|
-
packageMap.set(source.path, {
|
|
5109
|
-
...source,
|
|
5110
|
-
packageDir: destination,
|
|
5111
|
-
entryPath: remapPackageEntry(source, destination, source.entryPath),
|
|
5112
|
-
requireEntryPath: remapPackageEntry(source, destination, source.requireEntryPath)
|
|
4788
|
+
if (!root)
|
|
4789
|
+
return;
|
|
4790
|
+
const resolved = resolveBareSpecifierSync(args.path, dirname2(args.importer), root, args.kind === "require-call" || args.kind === "require-resolve" ? "require" : "import");
|
|
4791
|
+
return resolved ? { path: resolved } : undefined;
|
|
5113
4792
|
});
|
|
5114
4793
|
}
|
|
5115
|
-
const packages = [...packageMap.values()];
|
|
5116
|
-
const tree = {
|
|
5117
|
-
sourceRoot: verified.installRoot,
|
|
5118
|
-
packages,
|
|
5119
|
-
byPath: new Map(packages.map((pkg) => [pkg.path, pkg]))
|
|
5120
|
-
};
|
|
5121
|
-
const rootPackage = packageMap.get(verified.receipt.root.packagePath);
|
|
5122
|
-
if (!rootPackage)
|
|
5123
|
-
throw new Error("Verified root package is missing from the isolated tree.");
|
|
5124
|
-
const sourceRoot = verified.packages.find((pkg) => pkg.path === verified.receipt.root.packagePath);
|
|
5125
|
-
const entryPath = remapPackageEntry(sourceRoot, rootPackage.packageDir, verified.entryPath);
|
|
5126
|
-
if (!entryPath)
|
|
5127
|
-
throw new Error("Verified plugin entry is missing from the isolated tree.");
|
|
5128
|
-
await rewriteReachableModuleImports(entryPath, tree);
|
|
5129
|
-
return { loadRoot, entryPath, packages };
|
|
5130
|
-
} catch (error2) {
|
|
5131
|
-
await rm(loadRoot, { recursive: true, force: true }).catch(() => {});
|
|
5132
|
-
throw error2;
|
|
5133
|
-
}
|
|
5134
|
-
}
|
|
5135
|
-
async function isolateVendoredPlugin(verified) {
|
|
5136
|
-
const address = createHash2("sha256").update(verified.receipt.tree.digest).update("\x00").update(JSON.stringify(verified.receipt)).digest("hex");
|
|
5137
|
-
let pending = isolatedVendorTrees.get(address);
|
|
5138
|
-
if (!pending) {
|
|
5139
|
-
pending = createIsolatedVendorTree(verified, address).catch((error2) => {
|
|
5140
|
-
isolatedVendorTrees.delete(address);
|
|
5141
|
-
throw error2;
|
|
5142
|
-
});
|
|
5143
|
-
isolatedVendorTrees.set(address, pending);
|
|
5144
|
-
}
|
|
5145
|
-
const isolated = await pending;
|
|
5146
|
-
return {
|
|
5147
|
-
...verified,
|
|
5148
|
-
loadRoot: isolated.loadRoot,
|
|
5149
|
-
entryPath: isolated.entryPath,
|
|
5150
|
-
packages: isolated.packages
|
|
5151
|
-
};
|
|
5152
|
-
}
|
|
5153
|
-
async function snapshotAndIsolateVendoredPlugin(baseDir, packageName, version) {
|
|
5154
|
-
const sourceRoot = await resolveVendoredPluginInstallRoot(baseDir, packageName, version);
|
|
5155
|
-
if (!sourceRoot)
|
|
5156
|
-
return null;
|
|
5157
|
-
const base = await getIsolatedVendorBase();
|
|
5158
|
-
const container = await mkdtemp(join(base, "snapshot-"));
|
|
5159
|
-
const snapshotProject = join(container, "project");
|
|
5160
|
-
const snapshotRoot = vendoredNpmPluginRoot(snapshotProject, packageName, version);
|
|
5161
|
-
try {
|
|
5162
|
-
await mkdir(dirname2(snapshotRoot), { recursive: true });
|
|
5163
|
-
await cp(sourceRoot, snapshotRoot, {
|
|
5164
|
-
recursive: true,
|
|
5165
|
-
dereference: false,
|
|
5166
|
-
verbatimSymlinks: true
|
|
5167
|
-
});
|
|
5168
|
-
await vendorSnapshotHook?.(sourceRoot, snapshotRoot);
|
|
5169
|
-
const verifiedSnapshot = await verifyVendoredPlugin(snapshotProject, packageName, version);
|
|
5170
|
-
if (!verifiedSnapshot)
|
|
5171
|
-
return null;
|
|
5172
|
-
const isolated = await isolateVendoredPlugin(verifiedSnapshot);
|
|
5173
|
-
return { ...isolated, installRoot: sourceRoot };
|
|
5174
|
-
} finally {
|
|
5175
|
-
await rm(container, { recursive: true, force: true }).catch(() => {});
|
|
5176
|
-
}
|
|
5177
|
-
}
|
|
5178
|
-
function registerVendorCjsTree(verified) {
|
|
5179
|
-
ensureVendorCjsResolver();
|
|
5180
|
-
const key = `${verified.installRoot}\x00${verified.loadRoot}`;
|
|
5181
|
-
const previousKey = latestVendorCjsTree.get(verified.installRoot);
|
|
5182
|
-
if (vendorCjsTrees.has(key)) {
|
|
5183
|
-
latestVendorCjsTree.set(verified.installRoot, key);
|
|
5184
|
-
return () => {};
|
|
5185
|
-
}
|
|
5186
|
-
const packages = [...verified.packages].sort((a, b) => b.packageDir.length - a.packageDir.length);
|
|
5187
|
-
vendorCjsTrees.set(key, {
|
|
5188
|
-
sourceRoot: verified.installRoot,
|
|
5189
|
-
...previousKey ? { previousKey } : {},
|
|
5190
|
-
packages,
|
|
5191
|
-
byPath: new Map(packages.map((pkg) => [pkg.path, pkg]))
|
|
5192
4794
|
});
|
|
5193
|
-
latestVendorCjsTree.set(verified.installRoot, key);
|
|
5194
|
-
return () => {
|
|
5195
|
-
vendorCjsTrees.delete(key);
|
|
5196
|
-
if (latestVendorCjsTree.get(verified.installRoot) === key) {
|
|
5197
|
-
if (previousKey)
|
|
5198
|
-
latestVendorCjsTree.set(verified.installRoot, previousKey);
|
|
5199
|
-
else
|
|
5200
|
-
latestVendorCjsTree.delete(verified.installRoot);
|
|
5201
|
-
}
|
|
5202
|
-
};
|
|
5203
|
-
}
|
|
5204
|
-
function clearVendoredPluginResolver(baseDir, packageName, version) {
|
|
5205
|
-
let projectDir = baseDir;
|
|
5206
|
-
try {
|
|
5207
|
-
projectDir = realpathSync(baseDir);
|
|
5208
|
-
} catch {}
|
|
5209
|
-
const sourceRoot = vendoredNpmPluginRoot(projectDir, packageName, version);
|
|
5210
|
-
const key = latestVendorCjsTree.get(sourceRoot);
|
|
5211
|
-
if (!key)
|
|
5212
|
-
return;
|
|
5213
|
-
const tree = vendorCjsTrees.get(key);
|
|
5214
|
-
vendorCjsTrees.delete(key);
|
|
5215
|
-
if (tree?.previousKey)
|
|
5216
|
-
latestVendorCjsTree.set(sourceRoot, tree.previousKey);
|
|
5217
|
-
else
|
|
5218
|
-
latestVendorCjsTree.delete(sourceRoot);
|
|
5219
4795
|
}
|
|
5220
4796
|
async function loadNpmPackage(packageName, baseDir, version) {
|
|
5221
4797
|
if (version && isValidNpmPackageName(packageName) && isExactNpmVersion(version)) {
|
|
5222
|
-
const
|
|
5223
|
-
if (
|
|
5224
|
-
const
|
|
4798
|
+
const installRoot = await resolveVendoredPluginInstallRoot(baseDir, packageName, version);
|
|
4799
|
+
if (installRoot) {
|
|
4800
|
+
const packageDir = vendoredNpmPluginPackageDir(installRoot, packageName);
|
|
4801
|
+
let entry;
|
|
5225
4802
|
try {
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
if (verified.format === "commonjs") {
|
|
5230
|
-
return {
|
|
5231
|
-
module: createRequire(verified.entryPath)(verified.entryPath),
|
|
5232
|
-
moduleDir,
|
|
5233
|
-
packageDir,
|
|
5234
|
-
entryPath
|
|
5235
|
-
};
|
|
5236
|
-
}
|
|
5237
|
-
return {
|
|
5238
|
-
module: await import(pathToFileURL(verified.entryPath).href),
|
|
5239
|
-
moduleDir,
|
|
5240
|
-
packageDir,
|
|
5241
|
-
entryPath
|
|
5242
|
-
};
|
|
5243
|
-
} catch (error2) {
|
|
5244
|
-
restoreRegistration();
|
|
5245
|
-
throw error2;
|
|
4803
|
+
entry = await resolvePackageEntry(packageDir);
|
|
4804
|
+
} catch (cause) {
|
|
4805
|
+
throw new Error(`the downloaded copy of ${packageName}@${version} under plugins/npm/ is incomplete ` + `(${cause instanceof Error ? cause.message : String(cause)}). ` + `Reinstall it with \`gutterpress ext add ${packageName}@${version}\`.`, { cause });
|
|
5246
4806
|
}
|
|
4807
|
+
const entryPath = join(packageDir, ...entry.split("/"));
|
|
4808
|
+
vendoredRootsOpened.add(installRoot);
|
|
4809
|
+
installBunVendoredResolver();
|
|
4810
|
+
return {
|
|
4811
|
+
module: await import(pathToFileURL(entryPath).href),
|
|
4812
|
+
moduleDir: dirname2(entryPath),
|
|
4813
|
+
packageDir,
|
|
4814
|
+
entryPath
|
|
4815
|
+
};
|
|
5247
4816
|
}
|
|
5248
4817
|
}
|
|
5249
4818
|
try {
|
|
@@ -5325,11 +4894,6 @@ function ensureExitCleanupRegistered() {
|
|
|
5325
4894
|
unlinkSync(shadowPath);
|
|
5326
4895
|
} catch {}
|
|
5327
4896
|
}
|
|
5328
|
-
if (isolatedVendorBasePath) {
|
|
5329
|
-
try {
|
|
5330
|
-
rmSync(isolatedVendorBasePath, { recursive: true, force: true });
|
|
5331
|
-
} catch {}
|
|
5332
|
-
}
|
|
5333
4897
|
});
|
|
5334
4898
|
}
|
|
5335
4899
|
function shadowPathFor(pluginPath, mtimeMs) {
|
|
@@ -5605,7 +5169,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
|
|
|
5605
5169
|
for (const file of files) {
|
|
5606
5170
|
let refs;
|
|
5607
5171
|
try {
|
|
5608
|
-
refs = collectRenderedLocalRefs(await
|
|
5172
|
+
refs = collectRenderedLocalRefs(await readFile10(file, "utf8"));
|
|
5609
5173
|
} catch {
|
|
5610
5174
|
results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
|
|
5611
5175
|
continue;
|
|
@@ -5672,10 +5236,10 @@ import os from "node:os";
|
|
|
5672
5236
|
import fsp from "node:fs/promises";
|
|
5673
5237
|
|
|
5674
5238
|
// src/lib/missing-asset-placeholder.ts
|
|
5675
|
-
import { createHash as
|
|
5239
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5676
5240
|
import { deflateSync } from "node:zlib";
|
|
5677
5241
|
function placeholderOutputPath(missingOutputPath) {
|
|
5678
|
-
const hash =
|
|
5242
|
+
const hash = createHash2("sha256").update(missingOutputPath).digest("hex").slice(0, 16);
|
|
5679
5243
|
return `assets/gutterpress-missing/${hash}.png`;
|
|
5680
5244
|
}
|
|
5681
5245
|
function decodeHtmlAttribute(value) {
|
|
@@ -5956,7 +5520,7 @@ var check21 = {
|
|
|
5956
5520
|
registerCheck(check21);
|
|
5957
5521
|
|
|
5958
5522
|
// src/checks/source/accessibility-alt-text.ts
|
|
5959
|
-
import { readFile as
|
|
5523
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
5960
5524
|
var check22 = {
|
|
5961
5525
|
id: "source.accessibility.alt-text",
|
|
5962
5526
|
name: "Image Alt Text",
|
|
@@ -5970,7 +5534,7 @@ var check22 = {
|
|
|
5970
5534
|
const results = [];
|
|
5971
5535
|
for (const file of files) {
|
|
5972
5536
|
try {
|
|
5973
|
-
const content = await
|
|
5537
|
+
const content = await readFile11(file, "utf8");
|
|
5974
5538
|
const lines = content.split(`
|
|
5975
5539
|
`);
|
|
5976
5540
|
let inFence = false;
|
|
@@ -6014,7 +5578,7 @@ function extractImageAlts(line) {
|
|
|
6014
5578
|
registerCheck(check22);
|
|
6015
5579
|
|
|
6016
5580
|
// src/checks/source/accessibility-heading-order.ts
|
|
6017
|
-
import { readFile as
|
|
5581
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
6018
5582
|
var check23 = {
|
|
6019
5583
|
id: "source.accessibility.heading-order",
|
|
6020
5584
|
name: "Heading Order",
|
|
@@ -6028,7 +5592,7 @@ var check23 = {
|
|
|
6028
5592
|
const results = [];
|
|
6029
5593
|
for (const file of files) {
|
|
6030
5594
|
try {
|
|
6031
|
-
const content = await
|
|
5595
|
+
const content = await readFile12(file, "utf8");
|
|
6032
5596
|
const lines = content.split(`
|
|
6033
5597
|
`);
|
|
6034
5598
|
let inFence = false;
|
|
@@ -6064,7 +5628,7 @@ var check23 = {
|
|
|
6064
5628
|
registerCheck(check23);
|
|
6065
5629
|
|
|
6066
5630
|
// src/checks/source/layout-markers.ts
|
|
6067
|
-
import { readFile as
|
|
5631
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
6068
5632
|
var check24 = {
|
|
6069
5633
|
id: "source.markdown.layout-markers",
|
|
6070
5634
|
name: "Layout Markers",
|
|
@@ -6082,7 +5646,7 @@ var check24 = {
|
|
|
6082
5646
|
const md = createMarkdownRenderer(plugins);
|
|
6083
5647
|
for (const file of files) {
|
|
6084
5648
|
try {
|
|
6085
|
-
const content = await
|
|
5649
|
+
const content = await readFile13(file, "utf8");
|
|
6086
5650
|
const env = {};
|
|
6087
5651
|
md.render(content, env);
|
|
6088
5652
|
for (const w of env.layoutWarnings ?? []) {
|
|
@@ -6104,7 +5668,7 @@ var check24 = {
|
|
|
6104
5668
|
registerCheck(check24);
|
|
6105
5669
|
|
|
6106
5670
|
// src/checks/source/merge-markers.ts
|
|
6107
|
-
import { readFile as
|
|
5671
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
6108
5672
|
import path5 from "node:path";
|
|
6109
5673
|
|
|
6110
5674
|
// src/checks/asset/extensions.ts
|
|
@@ -6146,7 +5710,7 @@ var check25 = {
|
|
|
6146
5710
|
for (const file of textFiles) {
|
|
6147
5711
|
let content;
|
|
6148
5712
|
try {
|
|
6149
|
-
content = await
|
|
5713
|
+
content = await readFile14(file, "utf8");
|
|
6150
5714
|
} catch {
|
|
6151
5715
|
results.push(inspectionFailed(check25.id, `Could not read source file: ${file}`, { file }));
|
|
6152
5716
|
continue;
|
|
@@ -6209,7 +5773,7 @@ registerCheck(check25);
|
|
|
6209
5773
|
import { stat as stat3 } from "node:fs/promises";
|
|
6210
5774
|
|
|
6211
5775
|
// src/lib/image-inspect.ts
|
|
6212
|
-
import { stat as stat2, readFile as
|
|
5776
|
+
import { stat as stat2, readFile as readFile15 } from "node:fs/promises";
|
|
6213
5777
|
var DEFAULT_DPI = 72;
|
|
6214
5778
|
var cache = new Map;
|
|
6215
5779
|
async function inspectImage(path6) {
|
|
@@ -6224,7 +5788,7 @@ async function inspectImage(path6) {
|
|
|
6224
5788
|
return hit.info;
|
|
6225
5789
|
let info2 = null;
|
|
6226
5790
|
try {
|
|
6227
|
-
const buf = await
|
|
5791
|
+
const buf = await readFile15(path6);
|
|
6228
5792
|
info2 = parseImage(buf);
|
|
6229
5793
|
} catch {
|
|
6230
5794
|
info2 = null;
|
|
@@ -6784,7 +6348,7 @@ var check33 = {
|
|
|
6784
6348
|
registerCheck(check33);
|
|
6785
6349
|
|
|
6786
6350
|
// src/checks/heuristic/section-density.ts
|
|
6787
|
-
import { readFile as
|
|
6351
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
6788
6352
|
var check34 = {
|
|
6789
6353
|
id: "heuristic.chunking.section-density",
|
|
6790
6354
|
name: "Section Density",
|
|
@@ -6801,7 +6365,7 @@ var check34 = {
|
|
|
6801
6365
|
const results = [];
|
|
6802
6366
|
for (const file of files) {
|
|
6803
6367
|
try {
|
|
6804
|
-
const content = await
|
|
6368
|
+
const content = await readFile16(file, "utf8");
|
|
6805
6369
|
const lines = content.split(`
|
|
6806
6370
|
`);
|
|
6807
6371
|
let currentSectionStart = 0;
|
|
@@ -6998,7 +6562,7 @@ function artifactName(title, format) {
|
|
|
6998
6562
|
var BOOK_HTML_FILENAME = "book.html";
|
|
6999
6563
|
|
|
7000
6564
|
// src/lib/markdown/index.ts
|
|
7001
|
-
import { readdir as readdir2, readFile as
|
|
6565
|
+
import { readdir as readdir2, readFile as readFile17, writeFile, mkdir } from "node:fs/promises";
|
|
7002
6566
|
|
|
7003
6567
|
// src/lib/remote-auth/sync-messages.ts
|
|
7004
6568
|
function isOnlineSibling(filepath) {
|
|
@@ -7050,16 +6614,8 @@ async function assembleBookHtml(opts) {
|
|
|
7050
6614
|
}
|
|
7051
6615
|
for (const ref of env.imageRefs ?? [])
|
|
7052
6616
|
imageRefs.add(ref);
|
|
7053
|
-
|
|
7054
|
-
const safe = chapterId.replace(/&/g, "&").replace(/"/g, """);
|
|
7055
|
-
bodyContent += `<div class="gutterpress-chapter" data-chapter-src="${safe}">
|
|
7056
|
-
${rendered}
|
|
7057
|
-
</div>
|
|
7058
|
-
`;
|
|
7059
|
-
} else {
|
|
7060
|
-
bodyContent += rendered + `
|
|
6617
|
+
bodyContent += rendered + `
|
|
7061
6618
|
`;
|
|
7062
|
-
}
|
|
7063
6619
|
}
|
|
7064
6620
|
if (opts.onImageRefs) {
|
|
7065
6621
|
for (const ref of collectHtmlImageRefs(bodyContent))
|
|
@@ -7144,20 +6700,19 @@ ${css.trim()}
|
|
|
7144
6700
|
}
|
|
7145
6701
|
return assembleBookHtml({
|
|
7146
6702
|
files,
|
|
7147
|
-
readText: (relPath) =>
|
|
6703
|
+
readText: (relPath) => readFile17(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
7148
6704
|
projectCss: inlined.css,
|
|
7149
6705
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
7150
6706
|
title: opts.title,
|
|
7151
6707
|
plugins: opts.plugins,
|
|
7152
6708
|
pluginCss,
|
|
7153
|
-
wrapChapters: opts.wrapChapters,
|
|
7154
6709
|
annotateSourceChapters: opts.annotateSourceChapters,
|
|
7155
6710
|
onChapterWarnings: opts.onChapterWarnings,
|
|
7156
6711
|
onImageRefs: opts.onImageRefs
|
|
7157
6712
|
});
|
|
7158
6713
|
}
|
|
7159
6714
|
async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
7160
|
-
await
|
|
6715
|
+
await mkdir(outDir, { recursive: true });
|
|
7161
6716
|
const html = await renderChapters(inputDir, {
|
|
7162
6717
|
title: opts.title,
|
|
7163
6718
|
styles: opts.styles,
|
|
@@ -7170,12 +6725,12 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
|
7170
6725
|
onStyleWarnings: opts.onStyleWarnings
|
|
7171
6726
|
});
|
|
7172
6727
|
const outFile = join2(outDir, BOOK_HTML_FILENAME);
|
|
7173
|
-
await
|
|
6728
|
+
await writeFile(outFile, html);
|
|
7174
6729
|
return outFile;
|
|
7175
6730
|
}
|
|
7176
6731
|
|
|
7177
6732
|
// src/lib/markdownlint-fix.ts
|
|
7178
|
-
import { readFile as
|
|
6733
|
+
import { readFile as readFile18, writeFile as writeFile2 } from "node:fs/promises";
|
|
7179
6734
|
import { applyFixes } from "markdownlint";
|
|
7180
6735
|
import { lint as lint2 } from "markdownlint/sync";
|
|
7181
6736
|
async function uncommittedAmong(inputDir, files) {
|
|
@@ -7215,7 +6770,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
7215
6770
|
for (const [file, violations] of Object.entries(results)) {
|
|
7216
6771
|
if (!Array.isArray(violations) || violations.length === 0)
|
|
7217
6772
|
continue;
|
|
7218
|
-
const before = await
|
|
6773
|
+
const before = await readFile18(file, "utf8");
|
|
7219
6774
|
const after = applyFixes(before, violations);
|
|
7220
6775
|
if (after !== before)
|
|
7221
6776
|
pending.push({ file, content: after });
|
|
@@ -7226,7 +6781,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
7226
6781
|
}
|
|
7227
6782
|
const dirty = await uncommittedAmong(ctx.inputDir, pending.map((p) => p.file));
|
|
7228
6783
|
for (const { file, content } of pending) {
|
|
7229
|
-
await
|
|
6784
|
+
await writeFile2(file, content, "utf8");
|
|
7230
6785
|
}
|
|
7231
6786
|
log.warn(`--fix: rewrote ${pending.length} file(s):`);
|
|
7232
6787
|
for (const { file } of pending)
|
|
@@ -7719,8 +7274,8 @@ import { randomBytes } from "node:crypto";
|
|
|
7719
7274
|
|
|
7720
7275
|
// src/lib/build-fingerprint.ts
|
|
7721
7276
|
import * as fs from "node:fs";
|
|
7722
|
-
import { mkdir as
|
|
7723
|
-
import { createHash as
|
|
7277
|
+
import { mkdir as mkdir2, readFile as readFile19, writeFile as writeFile3 } from "node:fs/promises";
|
|
7278
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
7724
7279
|
import path7 from "node:path";
|
|
7725
7280
|
import git from "isomorphic-git";
|
|
7726
7281
|
var FINGERPRINT_FILENAME = "build-fingerprint.json";
|
|
@@ -7806,10 +7361,10 @@ async function getEngineBundleHash() {
|
|
|
7806
7361
|
getAssetPath("engine/gutterpress-agent.js")
|
|
7807
7362
|
]);
|
|
7808
7363
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
7809
|
-
|
|
7810
|
-
|
|
7364
|
+
readFile19(viewerPath),
|
|
7365
|
+
readFile19(agentPath)
|
|
7811
7366
|
]);
|
|
7812
|
-
const hash =
|
|
7367
|
+
const hash = createHash3("sha256");
|
|
7813
7368
|
hash.update(viewerBytes);
|
|
7814
7369
|
hash.update(agentBytes);
|
|
7815
7370
|
return hash.digest("hex").slice(0, 12);
|
|
@@ -7829,7 +7384,6 @@ async function getToolVersions() {
|
|
|
7829
7384
|
gutterpress: PACKAGE_META.version,
|
|
7830
7385
|
bun: process.versions.bun ?? null,
|
|
7831
7386
|
node: process.versions.node,
|
|
7832
|
-
"puppeteer-core": PACKAGE_META.dependencies["puppeteer-core"] ?? null,
|
|
7833
7387
|
engineBundle: engineBundleHash,
|
|
7834
7388
|
ghostscript: gsVersion,
|
|
7835
7389
|
qpdf: qpdfVersion,
|
|
@@ -7877,8 +7431,8 @@ async function writeBuildFingerprint(input) {
|
|
|
7877
7431
|
sourceRevision,
|
|
7878
7432
|
tools
|
|
7879
7433
|
};
|
|
7880
|
-
await
|
|
7881
|
-
await
|
|
7434
|
+
await mkdir2(outputDir, { recursive: true });
|
|
7435
|
+
await writeFile3(outPath, stableJsonStringify(payload), "utf8");
|
|
7882
7436
|
return outPath;
|
|
7883
7437
|
}
|
|
7884
7438
|
|
|
@@ -7887,12 +7441,9 @@ var INSTALL_HINTS2 = {
|
|
|
7887
7441
|
gs: INSTALL_HINTS.gs.body,
|
|
7888
7442
|
qpdf: INSTALL_HINTS.qpdf.body
|
|
7889
7443
|
};
|
|
7890
|
-
function rendersInPooledChromium(format, opts) {
|
|
7891
|
-
return format !== "html" && !opts.engineBrowser;
|
|
7892
|
-
}
|
|
7893
7444
|
async function preflightBuildTools(format, opts, config) {
|
|
7894
7445
|
const missing = [];
|
|
7895
|
-
if (
|
|
7446
|
+
if (format !== "html" && !opts.engineBrowser && !await resolveChromiumExecutable()) {
|
|
7896
7447
|
await requireChromiumExecutable();
|
|
7897
7448
|
}
|
|
7898
7449
|
if (format === "pdfx" && !await resolveGhostscript()) {
|
|
@@ -7916,24 +7467,6 @@ ${list}
|
|
|
7916
7467
|
|
|
7917
7468
|
Install the missing tools and re-run, or set GHOSTSCRIPT_PATH, CHROMIUM_PATH, or system PATH so gutterpress can find them. See the User Guide Chapter 7 (System Setup) at examples/gutterpress-user-guide/07-system-setup.md for the full per-feature matrix.`, 2);
|
|
7918
7469
|
}
|
|
7919
|
-
async function verifyNativeChromiumMilestone() {
|
|
7920
|
-
let version;
|
|
7921
|
-
try {
|
|
7922
|
-
const browser = await getBrowser(RENDER_TIMEOUT_MS);
|
|
7923
|
-
version = await browser.version();
|
|
7924
|
-
} catch (err) {
|
|
7925
|
-
throw new BuildError(`Could not launch a Chromium browser for the PDF build: ` + `${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7926
|
-
}
|
|
7927
|
-
const milestone = Number(/Chrome\/(\d+)/.exec(version)?.[1] ?? 0);
|
|
7928
|
-
if (milestone < REQUIRED_MILESTONE) {
|
|
7929
|
-
await closeBrowser();
|
|
7930
|
-
throw new BuildError(`The Gutterpress native engine requires Chromium ${REQUIRED_MILESTONE}+; found ${version}.
|
|
7931
|
-
|
|
7932
|
-
` + `Install a newer Chrome, Chromium, or Edge, or point CHROMIUM_PATH ` + `(or PUPPETEER_EXECUTABLE_PATH) at a ${REQUIRED_MILESTONE}+ binary.
|
|
7933
|
-
|
|
7934
|
-
` + `Note: this applies to the CLI's own Chromium only — the desktop app ` + `renders PDFs with its own bundled browser and needs no ` + `separate install.`, 2);
|
|
7935
|
-
}
|
|
7936
|
-
}
|
|
7937
7470
|
function computeGates(format, opts, config) {
|
|
7938
7471
|
if (format === "html") {
|
|
7939
7472
|
if (opts.skipLint || opts.skipPreValidate || opts.skipPostValidate) {
|
|
@@ -8182,7 +7715,9 @@ class HtmlOutput {
|
|
|
8182
7715
|
}
|
|
8183
7716
|
|
|
8184
7717
|
class PdfOutput {
|
|
8185
|
-
async finish(ctx, htmlFile) {
|
|
7718
|
+
async finish(ctx, htmlFile, browser) {
|
|
7719
|
+
if (!browser)
|
|
7720
|
+
throw new BuildError("PDF build reached the render stage without a browser");
|
|
8186
7721
|
const { workDir, outDir, inputDir, config, opts, format, manifestDir, gates } = ctx;
|
|
8187
7722
|
const pdfxMode = format === "pdfx" ? opts.pdfxFlavor ?? config.pdfx.flavor : undefined;
|
|
8188
7723
|
const pdfName = artifactName(config.title, pdfxMode ? "pdfx" : "pdf");
|
|
@@ -8193,13 +7728,13 @@ class PdfOutput {
|
|
|
8193
7728
|
const rawPdf = pdfxMode ? path8.join(stage, "raw.pdf") : path8.resolve(pdfFile);
|
|
8194
7729
|
await fsp2.mkdir(path8.dirname(path8.resolve(pdfFile)), { recursive: true });
|
|
8195
7730
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
8196
|
-
const { buildNativePdf } = await import("./engine-
|
|
7731
|
+
const { buildNativePdf } = await import("./engine-mwshptgf.js");
|
|
8197
7732
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
8198
7733
|
title: config.title,
|
|
8199
7734
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
8200
7735
|
signature: config.print.signature,
|
|
8201
7736
|
allowShrink: opts.allowShrink
|
|
8202
|
-
},
|
|
7737
|
+
}, browser);
|
|
8203
7738
|
for (const d of engineDiagnostics)
|
|
8204
7739
|
log.warn(d.message);
|
|
8205
7740
|
if (!pdfxMode) {
|
|
@@ -8266,20 +7801,26 @@ async function runBuild(opts) {
|
|
|
8266
7801
|
if (ctx.format !== "html") {
|
|
8267
7802
|
await preflightBuildTools(ctx.format, opts, ctx.config);
|
|
8268
7803
|
}
|
|
8269
|
-
|
|
8270
|
-
|
|
7804
|
+
const launch = opts.engineBrowser ?? (async () => (await import("./cdp-smdx6mr3.js")).launchChromium());
|
|
7805
|
+
const browserPromise = ctx.format === "html" ? null : launch();
|
|
7806
|
+
browserPromise?.catch(() => {});
|
|
8271
7807
|
try {
|
|
8272
7808
|
await fsp2.mkdir(ctx.workDir, { recursive: true });
|
|
8273
7809
|
await runQualityGates(ctx);
|
|
8274
|
-
|
|
8275
|
-
|
|
7810
|
+
let browser;
|
|
7811
|
+
if (browserPromise) {
|
|
7812
|
+
try {
|
|
7813
|
+
browser = await browserPromise;
|
|
7814
|
+
} catch (err) {
|
|
7815
|
+
throw new BuildError(`Could not launch a Chromium browser for the PDF build: ${err instanceof Error ? err.message : String(err)}`, 2);
|
|
7816
|
+
}
|
|
8276
7817
|
}
|
|
8277
7818
|
const htmlFile = await renderBook(ctx);
|
|
8278
7819
|
const strategy = ctx.format === "html" ? new HtmlOutput : new PdfOutput;
|
|
8279
|
-
return await strategy.finish(ctx, htmlFile);
|
|
7820
|
+
return await strategy.finish(ctx, htmlFile, browser);
|
|
8280
7821
|
} finally {
|
|
8281
|
-
if (
|
|
8282
|
-
await
|
|
7822
|
+
if (browserPromise)
|
|
7823
|
+
await browserPromise.then((b) => b.close(), () => {});
|
|
8283
7824
|
await fsp2.rm(ctx.workDir, { recursive: true, force: true });
|
|
8284
7825
|
}
|
|
8285
7826
|
}
|
|
@@ -8314,9 +7855,9 @@ function openPath(filePath) {
|
|
|
8314
7855
|
|
|
8315
7856
|
// src/preview/lifecycle.ts
|
|
8316
7857
|
import path10 from "path";
|
|
8317
|
-
import { tmpdir
|
|
7858
|
+
import { tmpdir } from "os";
|
|
8318
7859
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8319
|
-
import { readdir as readdir3, readFile as
|
|
7860
|
+
import { readdir as readdir3, readFile as readFile20, writeFile as writeFile4 } from "node:fs/promises";
|
|
8320
7861
|
|
|
8321
7862
|
// src/utils/file-utils.ts
|
|
8322
7863
|
import { promises as fs3 } from "fs";
|
|
@@ -8331,7 +7872,7 @@ async function fileExists(filePath) {
|
|
|
8331
7872
|
async function remove(targetPath) {
|
|
8332
7873
|
await fs3.rm(targetPath, { recursive: true, force: true });
|
|
8333
7874
|
}
|
|
8334
|
-
async function
|
|
7875
|
+
async function mkdir3(dirPath) {
|
|
8335
7876
|
await fs3.mkdir(dirPath, { recursive: true });
|
|
8336
7877
|
}
|
|
8337
7878
|
|
|
@@ -8355,9 +7896,6 @@ var EMPTY_BOOK_HTML = `<!doctype html>
|
|
|
8355
7896
|
</body>
|
|
8356
7897
|
</html>
|
|
8357
7898
|
`;
|
|
8358
|
-
function incrementalPreviewEnabled() {
|
|
8359
|
-
return process.env.GUTTERPRESS_PREVIEW_INCREMENTAL !== "0";
|
|
8360
|
-
}
|
|
8361
7899
|
async function renderPreviewBook(inputPath, config, opts) {
|
|
8362
7900
|
const { plugins, pluginStyles } = await loadPluginsWithCss(config.extensions, inputPath, (ref, err) => warn(`Skipping plugin "${ref}" in preview — ${err.message}`));
|
|
8363
7901
|
return renderChapters(inputPath, {
|
|
@@ -8366,7 +7904,6 @@ async function renderPreviewBook(inputPath, config, opts) {
|
|
|
8366
7904
|
files: opts.files,
|
|
8367
7905
|
plugins,
|
|
8368
7906
|
pluginStyles,
|
|
8369
|
-
wrapChapters: opts.wrapChapters,
|
|
8370
7907
|
annotateSourceChapters: true,
|
|
8371
7908
|
...opts.onCssAssets ? { onCssAssets: opts.onCssAssets } : {},
|
|
8372
7909
|
onChapterWarnings: (file, warnings) => {
|
|
@@ -8376,17 +7913,12 @@ async function renderPreviewBook(inputPath, config, opts) {
|
|
|
8376
7913
|
}
|
|
8377
7914
|
});
|
|
8378
7915
|
}
|
|
8379
|
-
function injectPreviewScripts(html
|
|
7916
|
+
function injectPreviewScripts(html) {
|
|
8380
7917
|
const scripts = ` <script src="/engine/gutterpress-viewer.js"></script>
|
|
8381
7918
|
` + `<script src="/preview/scripts/preview-interface.js"></script>
|
|
8382
7919
|
` + `<script src="/preview/scripts/preview-bridge.js"></script>
|
|
8383
7920
|
`;
|
|
8384
|
-
|
|
8385
|
-
if (pageIsolateChapters && /<\/head>/i.test(output)) {
|
|
8386
|
-
output = output.replace(/<\/head>/i, `<style>.gutterpress-chapter{break-before:page}</style>
|
|
8387
|
-
</head>`);
|
|
8388
|
-
}
|
|
8389
|
-
return output;
|
|
7921
|
+
return /<\/head>/i.test(html) ? html.replace(/<\/head>/i, scripts + "</head>") : html + scripts;
|
|
8390
7922
|
}
|
|
8391
7923
|
async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
8392
7924
|
if (!inputPath) {
|
|
@@ -8397,7 +7929,6 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
|
8397
7929
|
const nextAssets = new Map;
|
|
8398
7930
|
const html = await renderPreviewBook(inputPath, config, {
|
|
8399
7931
|
files: config.source?.files ?? null,
|
|
8400
|
-
wrapChapters: false,
|
|
8401
7932
|
onCssAssets: (copies) => {
|
|
8402
7933
|
for (const copy of copies)
|
|
8403
7934
|
nextAssets.set(copy.to, copy.from);
|
|
@@ -8406,39 +7937,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
|
8406
7937
|
cssAssets.clear();
|
|
8407
7938
|
for (const [to, from] of nextAssets)
|
|
8408
7939
|
cssAssets.set(to, from);
|
|
8409
|
-
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html
|
|
8410
|
-
}
|
|
8411
|
-
async function renderChapterPreviewHtml(inputPath, file, config) {
|
|
8412
|
-
const html = await renderPreviewBook(inputPath, config, {
|
|
8413
|
-
files: [canonicalChapterId(file)],
|
|
8414
|
-
wrapChapters: true
|
|
8415
|
-
});
|
|
8416
|
-
return injectPreviewScripts(html, true);
|
|
8417
|
-
}
|
|
8418
|
-
function describeChanges(changes, inputResolved) {
|
|
8419
|
-
const files = [];
|
|
8420
|
-
for (const [changedPath, event] of changes) {
|
|
8421
|
-
const relative2 = path9.relative(inputResolved, path9.resolve(changedPath));
|
|
8422
|
-
if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative2))
|
|
8423
|
-
continue;
|
|
8424
|
-
files.push({
|
|
8425
|
-
relativePath: canonicalChapterId(relative2),
|
|
8426
|
-
ext: path9.extname(changedPath).toLowerCase(),
|
|
8427
|
-
event
|
|
8428
|
-
});
|
|
8429
|
-
}
|
|
8430
|
-
return files;
|
|
8431
|
-
}
|
|
8432
|
-
function decideBroadcast(files, changeCount, incremental) {
|
|
8433
|
-
const only = files.length === 1 ? files[0] : null;
|
|
8434
|
-
if (incremental && changeCount === 1 && only?.ext === ".md" && only.event !== "unlink" && only.event !== "unlinkDir") {
|
|
8435
|
-
return {
|
|
8436
|
-
kind: "chapter-splice",
|
|
8437
|
-
chapterId: canonicalChapterId(only.relativePath),
|
|
8438
|
-
relativePath: only.relativePath
|
|
8439
|
-
};
|
|
8440
|
-
}
|
|
8441
|
-
return { kind: "full-reload" };
|
|
7940
|
+
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html), "utf-8");
|
|
8442
7941
|
}
|
|
8443
7942
|
function isDotPathUnderRoot(candidatePath, root) {
|
|
8444
7943
|
const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
@@ -8754,14 +8253,8 @@ function createFileWatcher(state) {
|
|
|
8754
8253
|
await generateAndWriteHtml(inputResolved, state.tempDir, updatedConfig, state.cssAssets);
|
|
8755
8254
|
if (closed)
|
|
8756
8255
|
return;
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
state.previewServer?.broadcastContentUpdate(decision.chapterId);
|
|
8760
|
-
info(`Chapter updated: ${decision.relativePath}`);
|
|
8761
|
-
} else {
|
|
8762
|
-
state.previewServer?.broadcastReload();
|
|
8763
|
-
info(changes.length > 1 ? `Preview updated (${changes.length} files changed — full reload)` : "Preview updated");
|
|
8764
|
-
}
|
|
8256
|
+
state.previewServer?.broadcastReload();
|
|
8257
|
+
info(changes.length > 1 ? `Preview updated (${changes.length} files changed)` : "Preview updated");
|
|
8765
8258
|
} catch (err) {
|
|
8766
8259
|
error("Failed to regenerate preview:", err);
|
|
8767
8260
|
} finally {
|
|
@@ -8819,7 +8312,7 @@ async function stopFileWatcher(state) {
|
|
|
8819
8312
|
}
|
|
8820
8313
|
|
|
8821
8314
|
// src/preview/lifecycle.ts
|
|
8822
|
-
var TEMP_DIR_BASE = path10.join(
|
|
8315
|
+
var TEMP_DIR_BASE = path10.join(tmpdir(), "gutterpress-preview");
|
|
8823
8316
|
var PID_FILE_NAME = ".gutterpress.pid";
|
|
8824
8317
|
function isProcessAlive(pid) {
|
|
8825
8318
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
@@ -8842,7 +8335,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
8842
8335
|
const dir = path10.join(TEMP_DIR_BASE, entry);
|
|
8843
8336
|
const pidFile = path10.join(dir, PID_FILE_NAME);
|
|
8844
8337
|
try {
|
|
8845
|
-
const raw = await
|
|
8338
|
+
const raw = await readFile20(pidFile, "utf8");
|
|
8846
8339
|
const pid = parseInt(raw.trim(), 10);
|
|
8847
8340
|
if (!isProcessAlive(pid)) {
|
|
8848
8341
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -8855,9 +8348,9 @@ async function initializePreviewDirectories() {
|
|
|
8855
8348
|
await cleanupOrphanTempDirs();
|
|
8856
8349
|
const tempDirSuffix = randomBytes2(8).toString("hex");
|
|
8857
8350
|
const tempDir = path10.join(TEMP_DIR_BASE, tempDirSuffix);
|
|
8858
|
-
await
|
|
8351
|
+
await mkdir3(tempDir);
|
|
8859
8352
|
debug(`Created temporary directory: ${tempDir}`);
|
|
8860
|
-
await
|
|
8353
|
+
await writeFile4(path10.join(tempDir, PID_FILE_NAME), `${process.pid}
|
|
8861
8354
|
`, "utf8");
|
|
8862
8355
|
return tempDir;
|
|
8863
8356
|
}
|
|
@@ -8947,8 +8440,8 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
8947
8440
|
// src/preview/http-server.ts
|
|
8948
8441
|
import http from "node:http";
|
|
8949
8442
|
import net from "node:net";
|
|
8950
|
-
import { createHash as
|
|
8951
|
-
import { readFile as
|
|
8443
|
+
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
8444
|
+
import { readFile as readFile21, stat as stat4 } from "node:fs/promises";
|
|
8952
8445
|
import path12 from "path";
|
|
8953
8446
|
import { WebSocket, WebSocketServer } from "ws";
|
|
8954
8447
|
|
|
@@ -9003,169 +8496,6 @@ function resolveStaticPath(urlPathname, root) {
|
|
|
9003
8496
|
|
|
9004
8497
|
// src/preview/http-server.ts
|
|
9005
8498
|
var HMR_PATH = "/__gutterpress-hmr";
|
|
9006
|
-
function hmrClientSnippet(initialRevision, instanceId) {
|
|
9007
|
-
return `
|
|
9008
|
-
<script>
|
|
9009
|
-
(function () {
|
|
9010
|
-
// When loaded by the preview SHELL (iframe double-buffer), the shell loads us
|
|
9011
|
-
// with ?gutterpressshell=1 and owns HMR (it swaps frames + syncs scroll), so we stay
|
|
9012
|
-
// inert. We must NOT bail merely because we're framed — other hosts (the
|
|
9013
|
-
// Electron desktop's SPA) embed book.html directly and rely on this HMR client
|
|
9014
|
-
// for scroll-anchor and reload.
|
|
9015
|
-
if (/[?&]gutterpressshell=1/.test(location.search)) return;
|
|
9016
|
-
var ANCHOR_KEY = 'gutterpress-scroll-anchor';
|
|
9017
|
-
var appliedRevision = ${initialRevision};
|
|
9018
|
-
var appliedInstance = ${JSON.stringify(instanceId)};
|
|
9019
|
-
var readyToAcknowledge = false;
|
|
9020
|
-
var reloadRequested = false;
|
|
9021
|
-
|
|
9022
|
-
// Find the element nearest the top of the viewport that carries a source
|
|
9023
|
-
// line (markdown-it-source-map emits data-source-line on block elements).
|
|
9024
|
-
// We anchor on SOURCE position, not pixels, because page breaks move when
|
|
9025
|
-
// content re-paginates.
|
|
9026
|
-
function captureAnchor() {
|
|
9027
|
-
var els = document.querySelectorAll('[data-source-line]');
|
|
9028
|
-
var best = null, bestTop = -Infinity;
|
|
9029
|
-
for (var i = 0; i < els.length; i++) {
|
|
9030
|
-
var r = els[i].getBoundingClientRect();
|
|
9031
|
-
if (r.bottom < 0 || r.height === 0) continue;
|
|
9032
|
-
if (r.top <= 80 && r.top > bestTop) { bestTop = r.top; best = els[i]; }
|
|
9033
|
-
}
|
|
9034
|
-
if (!best) {
|
|
9035
|
-
for (var j = 0; j < els.length; j++) {
|
|
9036
|
-
var rr = els[j].getBoundingClientRect();
|
|
9037
|
-
if (rr.bottom > 0 && rr.height > 0) { best = els[j]; break; }
|
|
9038
|
-
}
|
|
9039
|
-
}
|
|
9040
|
-
if (!best) return null;
|
|
9041
|
-
var chapter = best.closest && best.closest('[data-chapter-src]');
|
|
9042
|
-
return {
|
|
9043
|
-
chapter: chapter ? chapter.getAttribute('data-chapter-src') : null,
|
|
9044
|
-
line: best.getAttribute('data-source-line'),
|
|
9045
|
-
offset: best.getBoundingClientRect().top
|
|
9046
|
-
};
|
|
9047
|
-
}
|
|
9048
|
-
|
|
9049
|
-
// After a content reload, put the same source line back at the same viewport
|
|
9050
|
-
// offset so the author keeps their place (no jump to the top).
|
|
9051
|
-
function restoreAnchor() {
|
|
9052
|
-
var raw;
|
|
9053
|
-
try { raw = sessionStorage.getItem(ANCHOR_KEY); } catch (_) { return; }
|
|
9054
|
-
if (!raw) return;
|
|
9055
|
-
try { sessionStorage.removeItem(ANCHOR_KEY); } catch (_) {}
|
|
9056
|
-
var a; try { a = JSON.parse(raw); } catch (_) { return; }
|
|
9057
|
-
var tries = 0;
|
|
9058
|
-
(function attempt() {
|
|
9059
|
-
var blocks = document.querySelectorAll('[data-source-line]');
|
|
9060
|
-
var el = null;
|
|
9061
|
-
for (var i = 0; i < blocks.length; i++) {
|
|
9062
|
-
var chapter = blocks[i].closest && blocks[i].closest('[data-chapter-src]');
|
|
9063
|
-
var chapterId = chapter ? chapter.getAttribute('data-chapter-src') : null;
|
|
9064
|
-
if ((!a.chapter || chapterId === a.chapter) &&
|
|
9065
|
-
blocks[i].getAttribute('data-source-line') === String(a.line)) {
|
|
9066
|
-
el = blocks[i];
|
|
9067
|
-
break;
|
|
9068
|
-
}
|
|
9069
|
-
}
|
|
9070
|
-
if (el) {
|
|
9071
|
-
window.scrollBy({
|
|
9072
|
-
top: el.getBoundingClientRect().top - a.offset,
|
|
9073
|
-
behavior: 'instant'
|
|
9074
|
-
});
|
|
9075
|
-
if (window.previewAPI && typeof window.previewAPI.refresh === 'function') {
|
|
9076
|
-
window.previewAPI.refresh();
|
|
9077
|
-
}
|
|
9078
|
-
return;
|
|
9079
|
-
}
|
|
9080
|
-
if (tries++ < 240) setTimeout(attempt, 25); // wait for pagination
|
|
9081
|
-
})();
|
|
9082
|
-
}
|
|
9083
|
-
var restored = false;
|
|
9084
|
-
function restoreOnce() { if (restored) return; restored = true; restoreAnchor(); }
|
|
9085
|
-
function finishInitialRender() {
|
|
9086
|
-
restoreOnce();
|
|
9087
|
-
readyToAcknowledge = true;
|
|
9088
|
-
acknowledge();
|
|
9089
|
-
}
|
|
9090
|
-
// CRITICAL ordering: when the pagination engine is present it restructures
|
|
9091
|
-
// the DOM after load, so restoring the scroll anchor early would target
|
|
9092
|
-
// pre-pagination geometry. The Gutterpress engine viewer fires
|
|
9093
|
-
// 'gp:layout' when its pagination completes — wait for it. In static
|
|
9094
|
-
// mode (no engine) the content is final immediately, so restore right
|
|
9095
|
-
// after load.
|
|
9096
|
-
var hasEngine = !!document.querySelector('script[src*="/engine/gutterpress-viewer.js"]');
|
|
9097
|
-
window.addEventListener('gp:layout', finishInitialRender, { once: true });
|
|
9098
|
-
if (!hasEngine) {
|
|
9099
|
-
if (document.readyState === 'loading') {
|
|
9100
|
-
document.addEventListener('DOMContentLoaded', function () { setTimeout(finishInitialRender, 50); });
|
|
9101
|
-
} else {
|
|
9102
|
-
setTimeout(finishInitialRender, 50);
|
|
9103
|
-
}
|
|
9104
|
-
}
|
|
9105
|
-
var ws = null;
|
|
9106
|
-
var reconnectTimer = null;
|
|
9107
|
-
var reconnectDelay = 250;
|
|
9108
|
-
var stopped = false;
|
|
9109
|
-
|
|
9110
|
-
function acknowledge() {
|
|
9111
|
-
if (!readyToAcknowledge || !ws || ws.readyState !== 1) return;
|
|
9112
|
-
try {
|
|
9113
|
-
ws.send(JSON.stringify({
|
|
9114
|
-
type: 'reload-applied',
|
|
9115
|
-
instance: appliedInstance,
|
|
9116
|
-
revision: appliedRevision
|
|
9117
|
-
}));
|
|
9118
|
-
} catch (_) {}
|
|
9119
|
-
}
|
|
9120
|
-
|
|
9121
|
-
function connect() {
|
|
9122
|
-
if (stopped) return;
|
|
9123
|
-
ws = new WebSocket(location.origin.replace(/^http/, 'ws') + '${HMR_PATH}');
|
|
9124
|
-
ws.onopen = function () {
|
|
9125
|
-
reconnectDelay = 250;
|
|
9126
|
-
acknowledge();
|
|
9127
|
-
};
|
|
9128
|
-
ws.onmessage = function (e) {
|
|
9129
|
-
var msg;
|
|
9130
|
-
try { msg = JSON.parse(e.data); } catch (_) { return; }
|
|
9131
|
-
if (
|
|
9132
|
-
msg.type !== 'reload-state' &&
|
|
9133
|
-
msg.type !== 'full-reload' &&
|
|
9134
|
-
msg.type !== 'content-update'
|
|
9135
|
-
) return;
|
|
9136
|
-
var instance = typeof msg.instance === 'string' ? msg.instance : null;
|
|
9137
|
-
var revision = Number(msg.revision);
|
|
9138
|
-
if (!instance || !Number.isSafeInteger(revision) || revision < 0) return;
|
|
9139
|
-
if (instance === appliedInstance && revision <= appliedRevision) {
|
|
9140
|
-
acknowledge();
|
|
9141
|
-
return;
|
|
9142
|
-
}
|
|
9143
|
-
if (reloadRequested) return;
|
|
9144
|
-
reloadRequested = true;
|
|
9145
|
-
try { var a = captureAnchor(); if (a) sessionStorage.setItem(ANCHOR_KEY, JSON.stringify(a)); } catch (_) {}
|
|
9146
|
-
location.reload();
|
|
9147
|
-
};
|
|
9148
|
-
ws.onclose = function () {
|
|
9149
|
-
ws = null;
|
|
9150
|
-
if (stopped || reconnectTimer !== null) return;
|
|
9151
|
-
reconnectTimer = setTimeout(function () {
|
|
9152
|
-
reconnectTimer = null;
|
|
9153
|
-
connect();
|
|
9154
|
-
}, reconnectDelay);
|
|
9155
|
-
reconnectDelay = Math.min(reconnectDelay * 2, 5000);
|
|
9156
|
-
};
|
|
9157
|
-
ws.onerror = function () { try { ws.close(); } catch (_) {} };
|
|
9158
|
-
}
|
|
9159
|
-
|
|
9160
|
-
window.addEventListener('beforeunload', function () {
|
|
9161
|
-
stopped = true;
|
|
9162
|
-
if (reconnectTimer !== null) clearTimeout(reconnectTimer);
|
|
9163
|
-
});
|
|
9164
|
-
connect();
|
|
9165
|
-
})();
|
|
9166
|
-
</script>
|
|
9167
|
-
`;
|
|
9168
|
-
}
|
|
9169
8499
|
function shellHtml(initialRevision, instanceId) {
|
|
9170
8500
|
return `<!doctype html>
|
|
9171
8501
|
<html lang="en"><head><meta charset="utf-8"><title>gutterpress preview</title>
|
|
@@ -9218,14 +8548,7 @@ async function findAvailablePort(startPort, host = "127.0.0.1") {
|
|
|
9218
8548
|
}
|
|
9219
8549
|
throw new BuildError(`Could not find an available port on ${host} from ${firstPort} to ${port - 1}; all are already in use. ` + "Stop another preview server or pass --port with an available port.", undefined, { cause: lastBusyError });
|
|
9220
8550
|
}
|
|
9221
|
-
function
|
|
9222
|
-
const snippet = hmrClientSnippet(revision, instanceId);
|
|
9223
|
-
const closingBody = html.lastIndexOf("</body>");
|
|
9224
|
-
if (closingBody === -1)
|
|
9225
|
-
return html + snippet;
|
|
9226
|
-
return html.slice(0, closingBody) + snippet + html.slice(closingBody);
|
|
9227
|
-
}
|
|
9228
|
-
async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders = {}, hmrRevision = 0, hmrInstance = "") {
|
|
8551
|
+
async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders = {}) {
|
|
9229
8552
|
let filePath = absPath;
|
|
9230
8553
|
try {
|
|
9231
8554
|
const s = await stat4(filePath);
|
|
@@ -9238,7 +8561,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
9238
8561
|
}
|
|
9239
8562
|
let data;
|
|
9240
8563
|
try {
|
|
9241
|
-
data = await
|
|
8564
|
+
data = await readFile21(filePath);
|
|
9242
8565
|
} catch {
|
|
9243
8566
|
res.writeHead(404);
|
|
9244
8567
|
res.end("Not Found");
|
|
@@ -9246,9 +8569,8 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
9246
8569
|
}
|
|
9247
8570
|
const isHtml = filePath.endsWith(".html") || filePath.endsWith(".htm");
|
|
9248
8571
|
if (isHtml) {
|
|
9249
|
-
const withHmr = injectHmrClient(data.toString("utf-8"), hmrRevision, hmrInstance);
|
|
9250
8572
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
9251
|
-
res.end(
|
|
8573
|
+
res.end(data);
|
|
9252
8574
|
return;
|
|
9253
8575
|
}
|
|
9254
8576
|
const ct = STATIC_MIME[path12.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
@@ -9288,12 +8610,12 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
9288
8610
|
let data;
|
|
9289
8611
|
if (!validator || validator.stamp !== stamp) {
|
|
9290
8612
|
try {
|
|
9291
|
-
data = await
|
|
8613
|
+
data = await readFile21(absPath);
|
|
9292
8614
|
} catch {
|
|
9293
8615
|
await serveStatic(absPath, res);
|
|
9294
8616
|
return;
|
|
9295
8617
|
}
|
|
9296
|
-
const digest =
|
|
8618
|
+
const digest = createHash4("sha256").update(data).digest("base64url");
|
|
9297
8619
|
validator = { stamp, etag: `"gutterpress-file-${digest}"` };
|
|
9298
8620
|
validators.set(absPath, validator);
|
|
9299
8621
|
}
|
|
@@ -9323,16 +8645,11 @@ async function createPreviewServer(state, port) {
|
|
|
9323
8645
|
const projectAssetValidators = new Map;
|
|
9324
8646
|
let reloadRevision = 0;
|
|
9325
8647
|
const clients = new Map;
|
|
9326
|
-
function sendRevision(ws, type
|
|
8648
|
+
function sendRevision(ws, type) {
|
|
9327
8649
|
if (ws.readyState !== WebSocket.OPEN)
|
|
9328
8650
|
return;
|
|
9329
8651
|
try {
|
|
9330
|
-
ws.send(JSON.stringify({
|
|
9331
|
-
type,
|
|
9332
|
-
instance: instanceId,
|
|
9333
|
-
revision: reloadRevision,
|
|
9334
|
-
...file ? { file } : {}
|
|
9335
|
-
}));
|
|
8652
|
+
ws.send(JSON.stringify({ type, instance: instanceId, revision: reloadRevision }));
|
|
9336
8653
|
} catch {}
|
|
9337
8654
|
}
|
|
9338
8655
|
function waitForAcknowledgement(ws) {
|
|
@@ -9422,34 +8739,6 @@ async function createPreviewServer(state, port) {
|
|
|
9422
8739
|
res.end("Not Found");
|
|
9423
8740
|
return;
|
|
9424
8741
|
}
|
|
9425
|
-
if (url.pathname === "/__chapter" && req.method === "GET") {
|
|
9426
|
-
const rawFile = url.searchParams.get("file");
|
|
9427
|
-
const file = rawFile ? canonicalChapterId(rawFile) : "";
|
|
9428
|
-
const revision = Number(url.searchParams.get("revision"));
|
|
9429
|
-
const configuredFiles = state.config.source?.files;
|
|
9430
|
-
if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision || path12.extname(file).toLowerCase() !== ".md" || hasDotSegment(file) || !resolveWithinRoot(file, state.currentInputPath) || Array.isArray(configuredFiles) && configuredFiles.length > 0 && !configuredFiles.some((configured) => canonicalChapterId(configured) === file)) {
|
|
9431
|
-
res.writeHead(400);
|
|
9432
|
-
res.end("Bad Request");
|
|
9433
|
-
return;
|
|
9434
|
-
}
|
|
9435
|
-
try {
|
|
9436
|
-
const html = await renderChapterPreviewHtml(state.currentInputPath, file, state.config);
|
|
9437
|
-
if (revision !== reloadRevision) {
|
|
9438
|
-
res.writeHead(409);
|
|
9439
|
-
res.end("Superseded");
|
|
9440
|
-
return;
|
|
9441
|
-
}
|
|
9442
|
-
res.writeHead(200, {
|
|
9443
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
9444
|
-
"Cache-Control": "no-store"
|
|
9445
|
-
});
|
|
9446
|
-
res.end(html);
|
|
9447
|
-
} catch (error2) {
|
|
9448
|
-
res.writeHead(500);
|
|
9449
|
-
res.end(error2 instanceof Error ? error2.message : String(error2));
|
|
9450
|
-
}
|
|
9451
|
-
return;
|
|
9452
|
-
}
|
|
9453
8742
|
if (matchesEmbedded(url.pathname)) {
|
|
9454
8743
|
if (req.headers["if-none-match"] === EMBEDDED_ETAG) {
|
|
9455
8744
|
res.writeHead(304, { ETag: EMBEDDED_ETAG, "Cache-Control": EMBEDDED_CACHE_CONTROL });
|
|
@@ -9466,12 +8755,12 @@ async function createPreviewServer(state, port) {
|
|
|
9466
8755
|
}
|
|
9467
8756
|
return;
|
|
9468
8757
|
}
|
|
9469
|
-
if (url.pathname === "/"
|
|
8758
|
+
if (url.pathname === "/") {
|
|
9470
8759
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
9471
8760
|
res.end(shellHtml(reloadRevision, instanceId));
|
|
9472
8761
|
return;
|
|
9473
8762
|
}
|
|
9474
|
-
const pathname = url.pathname
|
|
8763
|
+
const pathname = url.pathname;
|
|
9475
8764
|
const cssAssetSource = state.cssAssets.get(pathname.replace(/^\/+/, ""));
|
|
9476
8765
|
if (cssAssetSource) {
|
|
9477
8766
|
await serveRevalidatedStatic(req, cssAssetSource, res, projectAssetValidators).catch((err) => {
|
|
@@ -9495,7 +8784,7 @@ async function createPreviewServer(state, port) {
|
|
|
9495
8784
|
res.end("Not Found");
|
|
9496
8785
|
return;
|
|
9497
8786
|
}
|
|
9498
|
-
const serve = servingProjectRoot ? serveRevalidatedStatic(req, absPath, res, projectAssetValidators) : serveStatic(absPath, res
|
|
8787
|
+
const serve = servingProjectRoot ? serveRevalidatedStatic(req, absPath, res, projectAssetValidators) : serveStatic(absPath, res);
|
|
9499
8788
|
await serve.catch((err) => {
|
|
9500
8789
|
if (!res.headersSent) {
|
|
9501
8790
|
res.writeHead(500);
|
|
@@ -9523,22 +8812,6 @@ async function createPreviewServer(state, port) {
|
|
|
9523
8812
|
openPath(serverUrl).catch(() => {});
|
|
9524
8813
|
}
|
|
9525
8814
|
let stopped = false;
|
|
9526
|
-
const broadcastUpdate = (type, file) => {
|
|
9527
|
-
if (stopped)
|
|
9528
|
-
return;
|
|
9529
|
-
reloadRevision++;
|
|
9530
|
-
for (const [ws] of clients) {
|
|
9531
|
-
sendRevision(ws, type, file);
|
|
9532
|
-
waitForAcknowledgement(ws);
|
|
9533
|
-
}
|
|
9534
|
-
};
|
|
9535
|
-
const updateInFlight = () => {
|
|
9536
|
-
for (const client of clients.values()) {
|
|
9537
|
-
if (client.acknowledgedRevision < reloadRevision)
|
|
9538
|
-
return true;
|
|
9539
|
-
}
|
|
9540
|
-
return false;
|
|
9541
|
-
};
|
|
9542
8815
|
return {
|
|
9543
8816
|
port: boundPort,
|
|
9544
8817
|
async close() {
|
|
@@ -9560,13 +8833,13 @@ async function createPreviewServer(state, port) {
|
|
|
9560
8833
|
});
|
|
9561
8834
|
},
|
|
9562
8835
|
broadcastReload() {
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
8836
|
+
if (stopped)
|
|
8837
|
+
return;
|
|
8838
|
+
reloadRevision++;
|
|
8839
|
+
for (const [ws] of clients) {
|
|
8840
|
+
sendRevision(ws, "full-reload");
|
|
8841
|
+
waitForAcknowledgement(ws);
|
|
8842
|
+
}
|
|
9570
8843
|
}
|
|
9571
8844
|
};
|
|
9572
8845
|
}
|
|
@@ -9627,14 +8900,14 @@ async function startPreviewServer(options) {
|
|
|
9627
8900
|
}
|
|
9628
8901
|
|
|
9629
8902
|
// src/lib/project-scaffold.ts
|
|
9630
|
-
import { access, copyFile, cp
|
|
8903
|
+
import { access, copyFile, cp, mkdir as mkdir5, readdir as readdir4, readFile as readFile23, stat as stat5, writeFile as writeFile5 } from "node:fs/promises";
|
|
9631
8904
|
import { constants as FS, existsSync as existsSync11 } from "node:fs";
|
|
9632
8905
|
import path14 from "node:path";
|
|
9633
8906
|
|
|
9634
8907
|
// src/lib/manifest-doc.ts
|
|
9635
8908
|
import { existsSync as existsSync10 } from "node:fs";
|
|
9636
8909
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
9637
|
-
import { mkdir as
|
|
8910
|
+
import { mkdir as mkdir4, open, readFile as readFile22, rename, rm } from "node:fs/promises";
|
|
9638
8911
|
import path13 from "node:path";
|
|
9639
8912
|
import { parseDocument, isSeq, YAMLSeq } from "yaml";
|
|
9640
8913
|
function resolveManifestPath(projectDir) {
|
|
@@ -9645,7 +8918,7 @@ async function loadManifestDoc(projectDir) {
|
|
|
9645
8918
|
const file = resolveManifestPath(projectDir);
|
|
9646
8919
|
let text = "";
|
|
9647
8920
|
try {
|
|
9648
|
-
text = await
|
|
8921
|
+
text = await readFile22(file, "utf8");
|
|
9649
8922
|
} catch (error2) {
|
|
9650
8923
|
if (error2.code !== "ENOENT")
|
|
9651
8924
|
throw error2;
|
|
@@ -9654,7 +8927,7 @@ async function loadManifestDoc(projectDir) {
|
|
|
9654
8927
|
}
|
|
9655
8928
|
async function writeManifestDoc(file, doc) {
|
|
9656
8929
|
const dir = path13.dirname(file);
|
|
9657
|
-
await
|
|
8930
|
+
await mkdir4(dir, { recursive: true });
|
|
9658
8931
|
const temporary = path13.join(dir, `.${path13.basename(file)}.tmp-${process.pid}-${randomUUID2()}`);
|
|
9659
8932
|
try {
|
|
9660
8933
|
const handle = await open(temporary, "wx", 438);
|
|
@@ -9671,7 +8944,7 @@ async function writeManifestDoc(file, doc) {
|
|
|
9671
8944
|
await directory.close();
|
|
9672
8945
|
}
|
|
9673
8946
|
} finally {
|
|
9674
|
-
await
|
|
8947
|
+
await rm(temporary, { force: true });
|
|
9675
8948
|
}
|
|
9676
8949
|
}
|
|
9677
8950
|
function ensureSeq(doc, key) {
|
|
@@ -9780,23 +9053,23 @@ async function scaffoldProject(options) {
|
|
|
9780
9053
|
const preset = customTemplateDir && options.preset === undefined ? undefined : requirePreset(options.preset, options.customPage);
|
|
9781
9054
|
const targets = preset ? resolveScaffoldTargets(options.targets, preset) : undefined;
|
|
9782
9055
|
try {
|
|
9783
|
-
await
|
|
9056
|
+
await mkdir5(projectDir, { recursive: true });
|
|
9784
9057
|
if (customTemplateDir) {
|
|
9785
9058
|
const entries = await readdir4(customTemplateDir, { withFileTypes: true });
|
|
9786
9059
|
for (const entry of entries) {
|
|
9787
9060
|
if (entry.name === ".gutterpress-template.json")
|
|
9788
9061
|
continue;
|
|
9789
|
-
await
|
|
9062
|
+
await cp(path14.join(customTemplateDir, entry.name), path14.join(projectDir, entry.name), { recursive: true });
|
|
9790
9063
|
}
|
|
9791
|
-
await
|
|
9064
|
+
await mkdir5(path14.join(projectDir, "assets"), { recursive: true });
|
|
9792
9065
|
} else {
|
|
9793
9066
|
const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
|
|
9794
9067
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
9795
|
-
await
|
|
9068
|
+
await mkdir5(path14.join(projectDir, "assets"), { recursive: true });
|
|
9796
9069
|
await copyFile(tplManifest, path14.join(projectDir, MANIFEST_FILENAMES[0]));
|
|
9797
9070
|
await copyFile(tplChapter, path14.join(projectDir, "chapter-01.md"));
|
|
9798
|
-
await
|
|
9799
|
-
await
|
|
9071
|
+
await mkdir5(path14.join(projectDir, "styles"), { recursive: true });
|
|
9072
|
+
await writeFile5(path14.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
|
|
9800
9073
|
}
|
|
9801
9074
|
} catch (e) {
|
|
9802
9075
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not create the book files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -9843,7 +9116,7 @@ async function scaffoldProject(options) {
|
|
|
9843
9116
|
let versionHistoryError;
|
|
9844
9117
|
if (requested === "local-git") {
|
|
9845
9118
|
try {
|
|
9846
|
-
const { providerFor } = await import("./source-provider-
|
|
9119
|
+
const { providerFor } = await import("./source-provider-pzmz0br9.js");
|
|
9847
9120
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
9848
9121
|
await provider.initVersionHistory({
|
|
9849
9122
|
projectDir,
|
|
@@ -9873,10 +9146,10 @@ async function ensureGitignoreHasDist(projectDir) {
|
|
|
9873
9146
|
const gitignorePath = path14.join(projectDir, ".gitignore");
|
|
9874
9147
|
let existing = null;
|
|
9875
9148
|
try {
|
|
9876
|
-
existing = await
|
|
9149
|
+
existing = await readFile23(gitignorePath, "utf8");
|
|
9877
9150
|
} catch {}
|
|
9878
9151
|
if (existing === null) {
|
|
9879
|
-
await
|
|
9152
|
+
await writeFile5(gitignorePath, `dist/
|
|
9880
9153
|
`, "utf8");
|
|
9881
9154
|
return;
|
|
9882
9155
|
}
|
|
@@ -9886,14 +9159,14 @@ async function ensureGitignoreHasDist(projectDir) {
|
|
|
9886
9159
|
return;
|
|
9887
9160
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith(`
|
|
9888
9161
|
`);
|
|
9889
|
-
await
|
|
9162
|
+
await writeFile5(gitignorePath, existing + (needsLeadingNewline ? `
|
|
9890
9163
|
` : "") + `dist/
|
|
9891
9164
|
`, "utf8");
|
|
9892
9165
|
}
|
|
9893
9166
|
async function firstSourceFile(manifestPath) {
|
|
9894
9167
|
let text;
|
|
9895
9168
|
try {
|
|
9896
|
-
text = await
|
|
9169
|
+
text = await readFile23(manifestPath, "utf8");
|
|
9897
9170
|
} catch {
|
|
9898
9171
|
return;
|
|
9899
9172
|
}
|
|
@@ -9905,15 +9178,15 @@ async function firstSourceFile(manifestPath) {
|
|
|
9905
9178
|
return m ? m[1].trim() : undefined;
|
|
9906
9179
|
}
|
|
9907
9180
|
async function fillTemplateFile(filePath, substitutions) {
|
|
9908
|
-
let text = await
|
|
9181
|
+
let text = await readFile23(filePath, "utf8");
|
|
9909
9182
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
9910
9183
|
text = text.split(token).join(value);
|
|
9911
9184
|
}
|
|
9912
|
-
await
|
|
9185
|
+
await writeFile5(filePath, text, "utf8");
|
|
9913
9186
|
}
|
|
9914
9187
|
|
|
9915
9188
|
// src/lib/extension-scaffold.ts
|
|
9916
|
-
import { access as access2, mkdir as
|
|
9189
|
+
import { access as access2, mkdir as mkdir6, readFile as readFile24, writeFile as writeFile6 } from "node:fs/promises";
|
|
9917
9190
|
import { constants as FS2 } from "node:fs";
|
|
9918
9191
|
import path15 from "node:path";
|
|
9919
9192
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -10025,14 +9298,14 @@ async function scaffoldExtension(options) {
|
|
|
10025
9298
|
const entries = TEMPLATE_FILES[kind];
|
|
10026
9299
|
const written = [];
|
|
10027
9300
|
try {
|
|
10028
|
-
await
|
|
9301
|
+
await mkdir6(extensionDir, { recursive: true });
|
|
10029
9302
|
for (const [assetName, relDest] of entries) {
|
|
10030
9303
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
10031
9304
|
const dest = path15.join(extensionDir, relDest);
|
|
10032
|
-
await
|
|
10033
|
-
const text = await
|
|
9305
|
+
await mkdir6(path15.dirname(dest), { recursive: true });
|
|
9306
|
+
const text = await readFile24(src, "utf8");
|
|
10034
9307
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
10035
|
-
await
|
|
9308
|
+
await writeFile6(dest, substitute(text, table), "utf8");
|
|
10036
9309
|
written.push(relDest);
|
|
10037
9310
|
}
|
|
10038
9311
|
} catch (e) {
|
|
@@ -10074,23 +9347,23 @@ var SKIP_ENTRIES = new Set([
|
|
|
10074
9347
|
]);
|
|
10075
9348
|
|
|
10076
9349
|
// src/lib/extension-manager.ts
|
|
10077
|
-
import { cp as
|
|
9350
|
+
import { cp as cp2, mkdir as mkdir8, readFile as readFile26, rm as rm3, stat as stat6 } from "node:fs/promises";
|
|
10078
9351
|
import { existsSync as existsSync12 } from "node:fs";
|
|
10079
9352
|
import path17 from "node:path";
|
|
10080
9353
|
import { isSeq as isSeq3, isMap as isMap2, isScalar as isScalar2, YAMLMap, Scalar } from "yaml";
|
|
10081
9354
|
|
|
10082
9355
|
// src/lib/npm-plugin-installer.ts
|
|
10083
|
-
import { createHash as
|
|
9356
|
+
import { createHash as createHash5, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
|
|
10084
9357
|
import { createReadStream } from "node:fs";
|
|
10085
9358
|
import {
|
|
10086
9359
|
lstat,
|
|
10087
|
-
mkdir as
|
|
10088
|
-
mkdtemp
|
|
9360
|
+
mkdir as mkdir7,
|
|
9361
|
+
mkdtemp,
|
|
10089
9362
|
open as open2,
|
|
10090
|
-
readFile as
|
|
9363
|
+
readFile as readFile25,
|
|
10091
9364
|
realpath,
|
|
10092
9365
|
rename as rename2,
|
|
10093
|
-
rm as
|
|
9366
|
+
rm as rm2
|
|
10094
9367
|
} from "node:fs/promises";
|
|
10095
9368
|
import path16 from "node:path";
|
|
10096
9369
|
import { Transform } from "node:stream";
|
|
@@ -10131,6 +9404,25 @@ async function withFetchTimeout(options, run) {
|
|
|
10131
9404
|
}
|
|
10132
9405
|
}
|
|
10133
9406
|
|
|
9407
|
+
// src/lib/npm-registry.ts
|
|
9408
|
+
var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
9409
|
+
var ENV_OVERRIDE = "GUTTERPRESS_NPM_REGISTRY";
|
|
9410
|
+
function npmRegistryUrl() {
|
|
9411
|
+
const configured = process.env[ENV_OVERRIDE]?.trim();
|
|
9412
|
+
if (!configured)
|
|
9413
|
+
return DEFAULT_NPM_REGISTRY;
|
|
9414
|
+
let scheme;
|
|
9415
|
+
try {
|
|
9416
|
+
scheme = new URL(configured).protocol;
|
|
9417
|
+
} catch {
|
|
9418
|
+
throw new Error(`Invalid ${ENV_OVERRIDE} URL "${configured}".`);
|
|
9419
|
+
}
|
|
9420
|
+
if (scheme !== "http:" && scheme !== "https:") {
|
|
9421
|
+
throw new Error(`${ENV_OVERRIDE} must be an http(s) URL — got "${scheme}".`);
|
|
9422
|
+
}
|
|
9423
|
+
return configured.replace(/\/+$/, "");
|
|
9424
|
+
}
|
|
9425
|
+
|
|
10134
9426
|
// src/lib/npm-plugin-installer.ts
|
|
10135
9427
|
var DEFAULT_LIMITS = {
|
|
10136
9428
|
metadataBytes: 15 * 1024 * 1024,
|
|
@@ -10345,7 +9637,7 @@ async function selectPackage(ctx, name, selector) {
|
|
|
10345
9637
|
}
|
|
10346
9638
|
async function downloadTarball(ctx, pkg, archivePath) {
|
|
10347
9639
|
const file = await open2(archivePath, "wx");
|
|
10348
|
-
const hash =
|
|
9640
|
+
const hash = createHash5(pkg.integrity.algorithm);
|
|
10349
9641
|
try {
|
|
10350
9642
|
await withFetchTimeout({
|
|
10351
9643
|
timeoutMs: TARBALL_TIMEOUT_MS,
|
|
@@ -10406,7 +9698,7 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
|
|
|
10406
9698
|
"OldGnuLongPath",
|
|
10407
9699
|
"OldExtendedHeader"
|
|
10408
9700
|
]);
|
|
10409
|
-
await
|
|
9701
|
+
await mkdir7(packageDir, { recursive: true });
|
|
10410
9702
|
const unpack = extract({
|
|
10411
9703
|
cwd: packageDir,
|
|
10412
9704
|
strip: 1,
|
|
@@ -10485,7 +9777,7 @@ async function readInstalledManifest(packageDir, selected) {
|
|
|
10485
9777
|
const info2 = await lstat(packageJsonPath);
|
|
10486
9778
|
if (!info2.isFile() || info2.isSymbolicLink())
|
|
10487
9779
|
throw new Error("not a regular file");
|
|
10488
|
-
manifest = object2(JSON.parse(await
|
|
9780
|
+
manifest = object2(JSON.parse(await readFile25(packageJsonPath, "utf8")));
|
|
10489
9781
|
} catch (error2) {
|
|
10490
9782
|
throw new SecurityInstallError(`Downloaded ${selected.name}@${selected.version} has no valid package.json (${error2 instanceof Error ? error2.message : String(error2)}).`);
|
|
10491
9783
|
}
|
|
@@ -10494,16 +9786,6 @@ async function readInstalledManifest(packageDir, selected) {
|
|
|
10494
9786
|
}
|
|
10495
9787
|
return manifest;
|
|
10496
9788
|
}
|
|
10497
|
-
async function optionalPackageEntry(packageDir, manifest, condition) {
|
|
10498
|
-
try {
|
|
10499
|
-
return await resolvePackageEntry(packageDir, manifest, condition);
|
|
10500
|
-
} catch (error2) {
|
|
10501
|
-
if (error2 instanceof Error && error2.message.includes("no contained JavaScript entry point")) {
|
|
10502
|
-
return;
|
|
10503
|
-
}
|
|
10504
|
-
throw error2;
|
|
10505
|
-
}
|
|
10506
|
-
}
|
|
10507
9789
|
function platformAllowed(rule, current) {
|
|
10508
9790
|
let values;
|
|
10509
9791
|
if (typeof rule === "string") {
|
|
@@ -10528,11 +9810,9 @@ function relativePackagePath(parentPackagePath, name) {
|
|
|
10528
9810
|
}
|
|
10529
9811
|
async function installPackage(ctx, name, selector, parentPackagePath, ancestors, depth, optional) {
|
|
10530
9812
|
const recordStart = ctx.records.length;
|
|
10531
|
-
const skippedStart = ctx.skipped.length;
|
|
10532
9813
|
const warningsBefore = new Set(ctx.warnings);
|
|
10533
9814
|
const discardOptionalSubtree = () => {
|
|
10534
9815
|
ctx.records.splice(recordStart);
|
|
10535
|
-
ctx.skipped.splice(skippedStart);
|
|
10536
9816
|
ctx.warnings = warningsBefore;
|
|
10537
9817
|
};
|
|
10538
9818
|
if (depth > ctx.limits.dependencyDepth) {
|
|
@@ -10558,39 +9838,35 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
10558
9838
|
const packageDir = path16.join(ctx.stageRoot, ...relativePath.split("/"));
|
|
10559
9839
|
const archivePath = path16.join(ctx.downloadsDir, `${ctx.archiveCounter++}.tgz`);
|
|
10560
9840
|
try {
|
|
10561
|
-
await
|
|
9841
|
+
await mkdir7(path16.dirname(packageDir), { recursive: true });
|
|
10562
9842
|
await downloadTarball(ctx, selected, archivePath);
|
|
10563
9843
|
await extractTarball(ctx, archivePath, packageDir, selected.name);
|
|
10564
9844
|
const manifest = await readInstalledManifest(packageDir, selected);
|
|
10565
9845
|
if (!platformAllowed(manifest.os, process.platform) || !platformAllowed(manifest.cpu, process.arch)) {
|
|
10566
9846
|
const reason = `${name}@${selected.version} does not support ${process.platform}/${process.arch}`;
|
|
10567
9847
|
if (optional) {
|
|
10568
|
-
await
|
|
9848
|
+
await rm2(packageDir, { recursive: true, force: true });
|
|
10569
9849
|
return { skipped: reason };
|
|
10570
9850
|
}
|
|
10571
9851
|
throw new PackageUnavailableError(reason);
|
|
10572
9852
|
}
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
9853
|
+
if (parentPackagePath === null) {
|
|
9854
|
+
try {
|
|
9855
|
+
await resolvePackageEntry(packageDir, manifest);
|
|
9856
|
+
} catch (cause) {
|
|
9857
|
+
throw new SecurityInstallError(`${name}@${selected.version} has no JavaScript plugin entry.`, { cause });
|
|
9858
|
+
}
|
|
10577
9859
|
}
|
|
10578
9860
|
const record = {
|
|
10579
9861
|
path: relativePath,
|
|
10580
9862
|
name,
|
|
10581
9863
|
version: selected.version,
|
|
10582
|
-
tarball: selected.tarball,
|
|
10583
|
-
integrity: selected.integrity.label,
|
|
10584
|
-
legacySha1: selected.integrity.legacySha1,
|
|
10585
|
-
...importEntry ? { entry: `${relativePath}/${importEntry}` } : {},
|
|
10586
|
-
...requireEntry ? { requireEntry: `${relativePath}/${requireEntry}` } : {},
|
|
10587
|
-
dependencies: {},
|
|
10588
9864
|
packageDir,
|
|
10589
9865
|
manifest
|
|
10590
9866
|
};
|
|
10591
9867
|
ctx.records.push(record);
|
|
10592
9868
|
if (selected.integrity.legacySha1) {
|
|
10593
|
-
ctx.warnings.add(`${name}@${selected.version} is an old npm release with only SHA-1 registry integrity. It was accepted for compatibility
|
|
9869
|
+
ctx.warnings.add(`${name}@${selected.version} is an old npm release with only SHA-1 registry integrity. It was accepted for compatibility.`);
|
|
10594
9870
|
}
|
|
10595
9871
|
const dependencies = stringMap(manifest.dependencies);
|
|
10596
9872
|
const optionalDependencies = stringMap(manifest.optionalDependencies);
|
|
@@ -10610,38 +9886,21 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
10610
9886
|
const child = await installPackage(ctx, dependency, range, relativePath, nextAncestors, depth + 1, false);
|
|
10611
9887
|
if (!child.path)
|
|
10612
9888
|
throw new Error(`Required dependency ${dependency} was unexpectedly skipped.`);
|
|
10613
|
-
record.dependencies[dependency] = child.path;
|
|
10614
9889
|
}
|
|
10615
9890
|
const optionalEntries = Object.entries(optionalDependencies).sort(([a], [b]) => a.localeCompare(b));
|
|
10616
9891
|
for (const [dependency, range] of optionalEntries) {
|
|
10617
|
-
|
|
10618
|
-
if (child.path)
|
|
10619
|
-
record.dependencies[dependency] = child.path;
|
|
10620
|
-
else if (child.skipped) {
|
|
10621
|
-
ctx.skipped.push({ from: relativePath, name: dependency, selector: range, kind: "optional", reason: child.skipped });
|
|
10622
|
-
}
|
|
10623
|
-
}
|
|
10624
|
-
for (const [dependency, range] of Object.entries(peerDependencies)) {
|
|
10625
|
-
if (object2(peerMeta[dependency])?.optional === true) {
|
|
10626
|
-
ctx.skipped.push({
|
|
10627
|
-
from: relativePath,
|
|
10628
|
-
name: dependency,
|
|
10629
|
-
selector: range,
|
|
10630
|
-
kind: "optional-peer",
|
|
10631
|
-
reason: "Optional peer dependencies are not auto-installed."
|
|
10632
|
-
});
|
|
10633
|
-
}
|
|
9892
|
+
await installPackage(ctx, dependency, range, relativePath, nextAncestors, depth + 1, true);
|
|
10634
9893
|
}
|
|
10635
9894
|
return { path: relativePath };
|
|
10636
9895
|
} catch (error2) {
|
|
10637
|
-
await
|
|
9896
|
+
await rm2(packageDir, { recursive: true, force: true });
|
|
10638
9897
|
if (optional && isOptionalAvailabilityError(error2)) {
|
|
10639
9898
|
discardOptionalSubtree();
|
|
10640
9899
|
return { skipped: error2 instanceof Error ? error2.message : String(error2) };
|
|
10641
9900
|
}
|
|
10642
9901
|
throw error2;
|
|
10643
9902
|
} finally {
|
|
10644
|
-
await
|
|
9903
|
+
await rm2(archivePath, { force: true });
|
|
10645
9904
|
}
|
|
10646
9905
|
}
|
|
10647
9906
|
async function ensureDirectory(pathname) {
|
|
@@ -10653,14 +9912,14 @@ async function ensureDirectory(pathname) {
|
|
|
10653
9912
|
} catch (error2) {
|
|
10654
9913
|
if (error2.code !== "ENOENT")
|
|
10655
9914
|
throw error2;
|
|
10656
|
-
await
|
|
9915
|
+
await mkdir7(pathname);
|
|
10657
9916
|
}
|
|
10658
9917
|
}
|
|
10659
9918
|
async function prepareVendorParent(projectDir, name) {
|
|
10660
9919
|
const plugins = path16.join(projectDir, PLUGINS_DIR);
|
|
10661
9920
|
const npm = path16.join(plugins, VENDORED_NPM_DIR);
|
|
10662
9921
|
const packageParent = path16.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
|
|
10663
|
-
await
|
|
9922
|
+
await mkdir7(projectDir, { recursive: true });
|
|
10664
9923
|
await ensureDirectory(plugins);
|
|
10665
9924
|
await ensureDirectory(npm);
|
|
10666
9925
|
await ensureDirectory(packageParent);
|
|
@@ -10673,29 +9932,16 @@ async function prepareVendorParent(projectDir, name) {
|
|
|
10673
9932
|
}
|
|
10674
9933
|
return realNpm;
|
|
10675
9934
|
}
|
|
10676
|
-
function publicReceipt(record) {
|
|
10677
|
-
return {
|
|
10678
|
-
path: record.path,
|
|
10679
|
-
name: record.name,
|
|
10680
|
-
version: record.version,
|
|
10681
|
-
tarball: record.tarball,
|
|
10682
|
-
integrity: record.integrity,
|
|
10683
|
-
legacySha1: record.legacySha1,
|
|
10684
|
-
...record.entry ? { entry: record.entry } : {},
|
|
10685
|
-
...record.requireEntry ? { requireEntry: record.requireEntry } : {},
|
|
10686
|
-
dependencies: record.dependencies
|
|
10687
|
-
};
|
|
10688
|
-
}
|
|
10689
9935
|
async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
10690
9936
|
const requestedProjectDir = path16.resolve(projectDirInput);
|
|
10691
|
-
await
|
|
9937
|
+
await mkdir7(requestedProjectDir, { recursive: true });
|
|
10692
9938
|
const projectDir = await realpath(requestedProjectDir);
|
|
10693
9939
|
const requested = parseNpmPluginSpec(packageSpec);
|
|
10694
9940
|
const npmRoot = await prepareVendorParent(projectDir, requested.name);
|
|
10695
|
-
const container = await
|
|
9941
|
+
const container = await mkdtemp(path16.join(npmRoot, ".install-"));
|
|
10696
9942
|
const stageRoot = path16.join(container, "root");
|
|
10697
9943
|
const downloadsDir = path16.join(container, "downloads");
|
|
10698
|
-
await Promise.all([
|
|
9944
|
+
await Promise.all([mkdir7(stageRoot), mkdir7(downloadsDir)]);
|
|
10699
9945
|
const ctx = {
|
|
10700
9946
|
fetch: options.fetch ?? globalThis.fetch,
|
|
10701
9947
|
signal: options.signal,
|
|
@@ -10704,7 +9950,6 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
10704
9950
|
downloadsDir,
|
|
10705
9951
|
packuments: new Map,
|
|
10706
9952
|
records: [],
|
|
10707
|
-
skipped: [],
|
|
10708
9953
|
warnings: new Set,
|
|
10709
9954
|
totalNetworkBytes: 0,
|
|
10710
9955
|
totalUnpackedBytes: 0,
|
|
@@ -10720,34 +9965,6 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
10720
9965
|
const rootRecord = ctx.records.find((record) => record.path === rootResult.path);
|
|
10721
9966
|
if (!rootRecord)
|
|
10722
9967
|
throw new Error("Root plugin package is missing from the dependency graph.");
|
|
10723
|
-
const importEntry = rootRecord.entry ?? rootRecord.requireEntry;
|
|
10724
|
-
if (!importEntry)
|
|
10725
|
-
throw new Error("Root plugin package has no recorded JavaScript entry.");
|
|
10726
|
-
const importFormat = packageEntryFormat(rootRecord.manifest, importEntry);
|
|
10727
|
-
const entry = importFormat === "commonjs" ? rootRecord.requireEntry ?? importEntry : importEntry;
|
|
10728
|
-
const format = packageEntryFormat(rootRecord.manifest, entry);
|
|
10729
|
-
const tree = await computeVendorTreeDigest(stageRoot);
|
|
10730
|
-
const receipt = {
|
|
10731
|
-
schemaVersion: VENDOR_RECEIPT_VERSION,
|
|
10732
|
-
root: {
|
|
10733
|
-
name: rootRecord.name,
|
|
10734
|
-
version: rootRecord.version,
|
|
10735
|
-
packagePath: rootRecord.path,
|
|
10736
|
-
entry,
|
|
10737
|
-
format
|
|
10738
|
-
},
|
|
10739
|
-
packages: ctx.records.map(publicReceipt).sort((a, b) => a.path.localeCompare(b.path)),
|
|
10740
|
-
skipped: ctx.skipped,
|
|
10741
|
-
tree: { algorithm: "sha256", ...tree }
|
|
10742
|
-
};
|
|
10743
|
-
const receiptFile = await open2(path16.join(stageRoot, VENDOR_RECEIPT_FILE), "wx");
|
|
10744
|
-
try {
|
|
10745
|
-
await receiptFile.writeFile(`${JSON.stringify(receipt, null, 2)}
|
|
10746
|
-
`, "utf8");
|
|
10747
|
-
await receiptFile.sync();
|
|
10748
|
-
} finally {
|
|
10749
|
-
await receiptFile.close();
|
|
10750
|
-
}
|
|
10751
9968
|
finalRoot = vendoredNpmPluginRoot(projectDir, rootRecord.name, rootRecord.version);
|
|
10752
9969
|
const realFinalParent = await realpath(path16.dirname(finalRoot));
|
|
10753
9970
|
if (!isContained(projectDir, realFinalParent)) {
|
|
@@ -10781,16 +9998,16 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
10781
9998
|
warnings: [...ctx.warnings]
|
|
10782
9999
|
};
|
|
10783
10000
|
} finally {
|
|
10784
|
-
await
|
|
10001
|
+
await rm2(container, { recursive: true, force: true }).catch(() => {});
|
|
10785
10002
|
}
|
|
10786
10003
|
}
|
|
10787
10004
|
async function finalizeNpmPluginInstall(installed) {
|
|
10788
10005
|
if (installed.backupRoot) {
|
|
10789
|
-
await
|
|
10006
|
+
await rm2(installed.backupRoot, { recursive: true, force: true });
|
|
10790
10007
|
}
|
|
10791
10008
|
}
|
|
10792
10009
|
async function rollbackNpmPluginInstall(installed) {
|
|
10793
|
-
await
|
|
10010
|
+
await rm2(installed.installRoot, { recursive: true, force: true });
|
|
10794
10011
|
if (installed.backupRoot)
|
|
10795
10012
|
await rename2(installed.backupRoot, installed.installRoot);
|
|
10796
10013
|
}
|
|
@@ -11066,11 +10283,10 @@ async function removeExtension(projectDir, use) {
|
|
|
11066
10283
|
return;
|
|
11067
10284
|
}
|
|
11068
10285
|
if (parsed.kind === "npm" && parsed.version) {
|
|
11069
|
-
await
|
|
10286
|
+
await rm3(vendoredNpmPluginRoot(projectDir, parsed.name, parsed.version), {
|
|
11070
10287
|
recursive: true,
|
|
11071
10288
|
force: true
|
|
11072
10289
|
});
|
|
11073
|
-
clearVendoredPluginResolver(projectDir, parsed.name, parsed.version);
|
|
11074
10290
|
}
|
|
11075
10291
|
});
|
|
11076
10292
|
}
|
|
@@ -11152,7 +10368,6 @@ async function addExtensionUnlocked(projectDir, specifier, options) {
|
|
|
11152
10368
|
} catch (cause) {
|
|
11153
10369
|
try {
|
|
11154
10370
|
await rollbackNpmPluginInstall(installed);
|
|
11155
|
-
clearVendoredPluginResolver(projectDir, installed.name, installed.version);
|
|
11156
10371
|
} catch (rollbackError) {
|
|
11157
10372
|
throw new Error(`Extension install failed and its previous vendor tree could not be restored: ` + `${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`, { cause });
|
|
11158
10373
|
}
|
|
@@ -11181,8 +10396,8 @@ async function addBuiltInStyleSet(projectDir, id) {
|
|
|
11181
10396
|
const dest = path17.join(projectDir, EXTENSIONS_DIR, id);
|
|
11182
10397
|
if (!existsSync12(dest)) {
|
|
11183
10398
|
const src = path17.join(await getAssetsDir(), "themes", id);
|
|
11184
|
-
await
|
|
11185
|
-
await
|
|
10399
|
+
await mkdir8(path17.dirname(dest), { recursive: true });
|
|
10400
|
+
await cp2(src, dest, { recursive: true });
|
|
11186
10401
|
}
|
|
11187
10402
|
return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
|
|
11188
10403
|
});
|
|
@@ -11190,8 +10405,8 @@ async function addBuiltInStyleSet(projectDir, id) {
|
|
|
11190
10405
|
|
|
11191
10406
|
// src/lib/extension-import.ts
|
|
11192
10407
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11193
|
-
import { cp as
|
|
11194
|
-
import { tmpdir as
|
|
10408
|
+
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as readFile27, rm as rm4, writeFile as writeFile7 } from "node:fs/promises";
|
|
10409
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
11195
10410
|
import path18 from "node:path";
|
|
11196
10411
|
import { unzipSync } from "fflate";
|
|
11197
10412
|
var MAX_THEME_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
@@ -11263,7 +10478,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11263
10478
|
if (!existsSync13(cssPath)) {
|
|
11264
10479
|
throw new Error("A theme must contain a theme.css file.");
|
|
11265
10480
|
}
|
|
11266
|
-
const css = await
|
|
10481
|
+
const css = await readFile27(cssPath, "utf8");
|
|
11267
10482
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11268
10483
|
if (reject) {
|
|
11269
10484
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11291,7 +10506,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11291
10506
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11292
10507
|
const rel = extraSheets[i];
|
|
11293
10508
|
const sheetPath = extraSheetPaths[i];
|
|
11294
|
-
const sheetCss = await
|
|
10509
|
+
const sheetCss = await readFile27(sheetPath, "utf8");
|
|
11295
10510
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11296
10511
|
if (sheetReject) {
|
|
11297
10512
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11311,8 +10526,8 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11311
10526
|
}
|
|
11312
10527
|
const id = await uniqueExtensionId(projectDir, meta.name?.trim() || fallbackName);
|
|
11313
10528
|
const dest = path18.join(projectDir, EXTENSIONS_DIR, id);
|
|
11314
|
-
await
|
|
11315
|
-
await
|
|
10529
|
+
await mkdir9(path18.dirname(dest), { recursive: true });
|
|
10530
|
+
await cp3(sourceDir, dest, { recursive: true });
|
|
11316
10531
|
await completeLandedManifest(dest, id, meta, metaExists);
|
|
11317
10532
|
const entry = await addExtension(projectDir, `./${EXTENSIONS_DIR}/${id}`);
|
|
11318
10533
|
return { entry, warnings };
|
|
@@ -11324,14 +10539,14 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
11324
10539
|
let pkg = {};
|
|
11325
10540
|
if (metaExists) {
|
|
11326
10541
|
try {
|
|
11327
|
-
const parsed = JSON.parse(await
|
|
10542
|
+
const parsed = JSON.parse(await readFile27(file, "utf8"));
|
|
11328
10543
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11329
10544
|
pkg = parsed;
|
|
11330
10545
|
}
|
|
11331
10546
|
} catch {}
|
|
11332
10547
|
}
|
|
11333
10548
|
const gutterpress = pkg.gutterpress && typeof pkg.gutterpress === "object" && !Array.isArray(pkg.gutterpress) ? pkg.gutterpress : {};
|
|
11334
|
-
await
|
|
10549
|
+
await writeFile7(file, JSON.stringify({
|
|
11335
10550
|
...pkg,
|
|
11336
10551
|
name: typeof pkg.name === "string" && pkg.name.trim() ? pkg.name : id,
|
|
11337
10552
|
gutterpress: { ...gutterpress, styles: ["theme.css"] }
|
|
@@ -11378,7 +10593,7 @@ async function importExtensionFromZip(projectDir, archive) {
|
|
|
11378
10593
|
throw new Error("No theme.css found in the package. A theme needs a theme.css at the package root or inside a single folder.");
|
|
11379
10594
|
}
|
|
11380
10595
|
const prefix = root === "" ? "" : `${root}/`;
|
|
11381
|
-
const tmp = await
|
|
10596
|
+
const tmp = await mkdtemp2(path18.join(tmpdir2(), "gutterpress-theme-"));
|
|
11382
10597
|
try {
|
|
11383
10598
|
const relPaths = [];
|
|
11384
10599
|
for (const [name, data] of Object.entries(files)) {
|
|
@@ -11395,12 +10610,12 @@ async function importExtensionFromZip(projectDir, archive) {
|
|
|
11395
10610
|
throw new Error(`The package contains an unsafe path and was rejected: ${name}`);
|
|
11396
10611
|
}
|
|
11397
10612
|
relPaths.push(rel);
|
|
11398
|
-
await
|
|
11399
|
-
await
|
|
10613
|
+
await mkdir9(path18.dirname(dest), { recursive: true });
|
|
10614
|
+
await writeFile7(dest, data);
|
|
11400
10615
|
}
|
|
11401
10616
|
return await finalizeExtensionImport(projectDir, tmp, relPaths, root || "imported-look");
|
|
11402
10617
|
} finally {
|
|
11403
|
-
await
|
|
10618
|
+
await rm4(tmp, { recursive: true, force: true });
|
|
11404
10619
|
}
|
|
11405
10620
|
}
|
|
11406
10621
|
async function importExtensionFromCssText(projectDir, css, name) {
|
|
@@ -11409,10 +10624,10 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11409
10624
|
throw new Error(`The CSS could not be parsed — ${reject.message}`);
|
|
11410
10625
|
}
|
|
11411
10626
|
const displayName = name.trim() || "Imported theme";
|
|
11412
|
-
const tmp = await
|
|
10627
|
+
const tmp = await mkdtemp2(path18.join(tmpdir2(), "gutterpress-theme-"));
|
|
11413
10628
|
try {
|
|
11414
|
-
await
|
|
11415
|
-
await
|
|
10629
|
+
await writeFile7(path18.join(tmp, "theme.css"), css, "utf8");
|
|
10630
|
+
await writeFile7(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify({ name: slugify(displayName, "look"), gutterpress: { styles: ["theme.css"] } }, null, 2), "utf8");
|
|
11416
10631
|
const result = await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], displayName);
|
|
11417
10632
|
result.warnings.unshift({
|
|
11418
10633
|
code: "no-theme-json",
|
|
@@ -11420,17 +10635,17 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11420
10635
|
});
|
|
11421
10636
|
return result;
|
|
11422
10637
|
} finally {
|
|
11423
|
-
await
|
|
10638
|
+
await rm4(tmp, { recursive: true, force: true });
|
|
11424
10639
|
}
|
|
11425
10640
|
}
|
|
11426
10641
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
11427
10642
|
const ext = path18.extname(filePath).toLowerCase();
|
|
11428
10643
|
if (ext === ".zip") {
|
|
11429
|
-
const buf = await
|
|
10644
|
+
const buf = await readFile27(filePath);
|
|
11430
10645
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
11431
10646
|
}
|
|
11432
10647
|
if (ext === ".css") {
|
|
11433
|
-
const css = await
|
|
10648
|
+
const css = await readFile27(filePath, "utf8");
|
|
11434
10649
|
return importExtensionFromCssText(projectDir, css, prettify(path18.basename(filePath, ".css")));
|
|
11435
10650
|
}
|
|
11436
10651
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -11493,9 +10708,9 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
11493
10708
|
if (!css.trim())
|
|
11494
10709
|
throw new Error(`Fetched CSS from ${trimmed} was empty.`);
|
|
11495
10710
|
assertLooksLikeCss(css, trimmed);
|
|
11496
|
-
const tmp = await
|
|
10711
|
+
const tmp = await mkdtemp2(path18.join(tmpdir2(), "gutterpress-ext-"));
|
|
11497
10712
|
try {
|
|
11498
|
-
await
|
|
10713
|
+
await writeFile7(path18.join(tmp, "theme.css"), css, "utf8");
|
|
11499
10714
|
const gp = meta.gutterpress && typeof meta.gutterpress === "object" && !Array.isArray(meta.gutterpress) ? meta.gutterpress : {};
|
|
11500
10715
|
const finalMeta = {
|
|
11501
10716
|
name: typeof meta.name === "string" && meta.name || slugify(baseName, "look"),
|
|
@@ -11507,10 +10722,10 @@ async function importExtensionFromUrl(projectDir, url) {
|
|
|
11507
10722
|
preview: typeof gp.preview === "string" ? gp.preview : null
|
|
11508
10723
|
}
|
|
11509
10724
|
};
|
|
11510
|
-
await
|
|
10725
|
+
await writeFile7(path18.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify(finalMeta, null, 2), "utf8");
|
|
11511
10726
|
return await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], baseName);
|
|
11512
10727
|
} finally {
|
|
11513
|
-
await
|
|
10728
|
+
await rm4(tmp, { recursive: true, force: true });
|
|
11514
10729
|
}
|
|
11515
10730
|
}
|
|
11516
10731
|
|
|
@@ -12133,11 +11348,11 @@ ${tail}` : ""}`);
|
|
|
12133
11348
|
};
|
|
12134
11349
|
|
|
12135
11350
|
// src/lib/publish/providers/guided.ts
|
|
12136
|
-
import { copyFile as copyFile2, mkdir as
|
|
11351
|
+
import { copyFile as copyFile2, mkdir as mkdir10, writeFile as writeFile8 } from "node:fs/promises";
|
|
12137
11352
|
import path19 from "node:path";
|
|
12138
11353
|
async function stageGuidedPackage(req, providerId, listingLines) {
|
|
12139
11354
|
const packageDir = path19.join(path19.dirname(req.artifact.path), "publish", providerId);
|
|
12140
|
-
await
|
|
11355
|
+
await mkdir10(packageDir, { recursive: true });
|
|
12141
11356
|
await copyFile2(req.artifact.path, path19.join(packageDir, path19.basename(req.artifact.path)));
|
|
12142
11357
|
const { title, authors } = req.project;
|
|
12143
11358
|
const sheet = [
|
|
@@ -12151,7 +11366,7 @@ async function stageGuidedPackage(req, providerId, listingLines) {
|
|
|
12151
11366
|
""
|
|
12152
11367
|
].join(`
|
|
12153
11368
|
`);
|
|
12154
|
-
await
|
|
11369
|
+
await writeFile8(path19.join(packageDir, "LISTING.md"), sheet, "utf8");
|
|
12155
11370
|
return packageDir;
|
|
12156
11371
|
}
|
|
12157
11372
|
|
|
@@ -12214,8 +11429,8 @@ var drivethrurpgProvider = {
|
|
|
12214
11429
|
};
|
|
12215
11430
|
|
|
12216
11431
|
// src/lib/publish/providers/gdrive.ts
|
|
12217
|
-
import { mkdtemp as
|
|
12218
|
-
import { tmpdir as
|
|
11432
|
+
import { mkdtemp as mkdtemp3, readFile as readFile28, readdir as readdir5, rm as rm5, stat as stat7, writeFile as writeFile9 } from "node:fs/promises";
|
|
11433
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
12219
11434
|
import path20 from "node:path";
|
|
12220
11435
|
import { Zip, ZipDeflate } from "fflate";
|
|
12221
11436
|
|
|
@@ -12606,12 +11821,12 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
12606
11821
|
await collectZipEntries(root, full, out, onWarn);
|
|
12607
11822
|
} else if (entry.isFile()) {
|
|
12608
11823
|
const rel = path20.relative(root, full).split(path20.sep).join("/");
|
|
12609
|
-
out[rel] = await
|
|
11824
|
+
out[rel] = await readFile28(full);
|
|
12610
11825
|
} else if (entry.isSymbolicLink()) {
|
|
12611
11826
|
const resolved = await stat7(full).catch(() => null);
|
|
12612
11827
|
if (resolved?.isFile()) {
|
|
12613
11828
|
const rel = path20.relative(root, full).split(path20.sep).join("/");
|
|
12614
|
-
out[rel] = await
|
|
11829
|
+
out[rel] = await readFile28(full);
|
|
12615
11830
|
} else if (resolved?.isDirectory()) {
|
|
12616
11831
|
await collectZipEntries(root, full, out, onWarn);
|
|
12617
11832
|
} else {
|
|
@@ -12622,11 +11837,11 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
12622
11837
|
}
|
|
12623
11838
|
}
|
|
12624
11839
|
}
|
|
12625
|
-
async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl =
|
|
11840
|
+
async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeFile9) {
|
|
12626
11841
|
const entries = {};
|
|
12627
11842
|
await collectZipEntries(exportDir, exportDir, entries, onWarn);
|
|
12628
11843
|
const fileName = `${bookSlug(title)}-website.zip`;
|
|
12629
|
-
const tmpRoot = await
|
|
11844
|
+
const tmpRoot = await mkdtemp3(path20.join(tmpdir3(), "gutterpress-gdrive-"));
|
|
12630
11845
|
try {
|
|
12631
11846
|
const filePath = path20.join(tmpRoot, fileName);
|
|
12632
11847
|
const zipped = await zipEntriesNonBlocking(entries, { level: 6 });
|
|
@@ -12635,10 +11850,10 @@ async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeF
|
|
|
12635
11850
|
filePath,
|
|
12636
11851
|
fileName,
|
|
12637
11852
|
mimeType: "application/zip",
|
|
12638
|
-
cleanup: () =>
|
|
11853
|
+
cleanup: () => rm5(tmpRoot, { recursive: true, force: true })
|
|
12639
11854
|
};
|
|
12640
11855
|
} catch (e) {
|
|
12641
|
-
await
|
|
11856
|
+
await rm5(tmpRoot, { recursive: true, force: true }).catch(() => {});
|
|
12642
11857
|
throw e;
|
|
12643
11858
|
}
|
|
12644
11859
|
}
|
|
@@ -12726,7 +11941,7 @@ var gdriveProvider = {
|
|
|
12726
11941
|
};
|
|
12727
11942
|
|
|
12728
11943
|
// src/lib/publish/butler.ts
|
|
12729
|
-
import { chmod, mkdir as
|
|
11944
|
+
import { chmod, mkdir as mkdir11, stat as stat8, writeFile as writeFile10 } from "node:fs/promises";
|
|
12730
11945
|
import path21 from "node:path";
|
|
12731
11946
|
import { unzipSync as unzipSync2 } from "fflate";
|
|
12732
11947
|
var BUTLER_DOWNLOAD_TIMEOUT_MS = 300000;
|
|
@@ -12787,13 +12002,13 @@ async function ensureButler(deps) {
|
|
|
12787
12002
|
return new Uint8Array(await response.arrayBuffer());
|
|
12788
12003
|
});
|
|
12789
12004
|
const files = unzipSync2(archive);
|
|
12790
|
-
await
|
|
12005
|
+
await mkdir11(cacheDir, { recursive: true });
|
|
12791
12006
|
let foundBinary = false;
|
|
12792
12007
|
for (const [name, data] of Object.entries(files)) {
|
|
12793
12008
|
if (name.endsWith("/"))
|
|
12794
12009
|
continue;
|
|
12795
12010
|
const target = path21.join(cacheDir, path21.basename(name));
|
|
12796
|
-
await
|
|
12011
|
+
await writeFile10(target, data);
|
|
12797
12012
|
if (path21.basename(name) === butlerBinaryName()) {
|
|
12798
12013
|
await chmod(target, 493).catch(() => {});
|
|
12799
12014
|
foundBinary = true;
|
|
@@ -12990,7 +12205,7 @@ var kdpProvider = {
|
|
|
12990
12205
|
};
|
|
12991
12206
|
|
|
12992
12207
|
// src/lib/publish/providers/local-folder.ts
|
|
12993
|
-
import { cp as
|
|
12208
|
+
import { cp as cp4, mkdir as mkdir12, stat as stat9 } from "node:fs/promises";
|
|
12994
12209
|
import path22 from "node:path";
|
|
12995
12210
|
var LOCAL_PUBLISH_DEFAULT_DIR = "dist";
|
|
12996
12211
|
var info7 = {
|
|
@@ -13028,10 +12243,10 @@ var localFolderProvider = {
|
|
|
13028
12243
|
},
|
|
13029
12244
|
async upload(req) {
|
|
13030
12245
|
const dir = localPublishDir(req);
|
|
13031
|
-
await
|
|
12246
|
+
await mkdir12(dir, { recursive: true });
|
|
13032
12247
|
const dest = req.artifact.format === "html" ? dir : path22.join(dir, path22.basename(req.artifact.path));
|
|
13033
12248
|
if (path22.resolve(dest) !== path22.resolve(req.artifact.path)) {
|
|
13034
|
-
await
|
|
12249
|
+
await cp4(req.artifact.path, dest, { recursive: true });
|
|
13035
12250
|
}
|
|
13036
12251
|
return { kind: "published", detail: `Saved to ${dest}` };
|
|
13037
12252
|
}
|
|
@@ -13595,7 +12810,7 @@ import git7 from "isomorphic-git";
|
|
|
13595
12810
|
// src/lib/remote-auth/scorched-earth.ts
|
|
13596
12811
|
import git8 from "isomorphic-git";
|
|
13597
12812
|
// src/lib/publish/selections.ts
|
|
13598
|
-
import { chmod as chmod2, mkdir as
|
|
12813
|
+
import { chmod as chmod2, mkdir as mkdir13, readFile as readFile29, writeFile as writeFile11 } from "node:fs/promises";
|
|
13599
12814
|
import path24 from "node:path";
|
|
13600
12815
|
class PublishSelectionsStore {
|
|
13601
12816
|
filePath;
|
|
@@ -13605,7 +12820,7 @@ class PublishSelectionsStore {
|
|
|
13605
12820
|
}
|
|
13606
12821
|
async read() {
|
|
13607
12822
|
try {
|
|
13608
|
-
const raw = await
|
|
12823
|
+
const raw = await readFile29(this.filePath, "utf8");
|
|
13609
12824
|
const parsed = JSON.parse(raw);
|
|
13610
12825
|
if (parsed && typeof parsed === "object") {
|
|
13611
12826
|
return {
|
|
@@ -13618,8 +12833,8 @@ class PublishSelectionsStore {
|
|
|
13618
12833
|
return { version: 1, global: {}, projects: {} };
|
|
13619
12834
|
}
|
|
13620
12835
|
async write(data) {
|
|
13621
|
-
await
|
|
13622
|
-
await
|
|
12836
|
+
await mkdir13(path24.dirname(this.filePath), { recursive: true });
|
|
12837
|
+
await writeFile11(this.filePath, JSON.stringify(data, null, 2), {
|
|
13623
12838
|
encoding: "utf8",
|
|
13624
12839
|
mode: 384
|
|
13625
12840
|
});
|
|
@@ -13684,78 +12899,4 @@ function normalizeProjectKey(projectDir) {
|
|
|
13684
12899
|
}
|
|
13685
12900
|
// src/lib/publish/manifest-publish.ts
|
|
13686
12901
|
import { isMap as isMap4 } from "yaml";
|
|
13687
|
-
|
|
13688
|
-
import { resolve as resolve8 } from "node:path";
|
|
13689
|
-
import { readFile as readFile31 } from "node:fs/promises";
|
|
13690
|
-
async function runLint(opts = {}) {
|
|
13691
|
-
const { glob } = await import("glob");
|
|
13692
|
-
const { manifest, manifestDir } = await loadManifestWithPath(opts.manifest, {
|
|
13693
|
-
explicit: opts.manifest !== undefined
|
|
13694
|
-
});
|
|
13695
|
-
const resolved = resolveConfig({}, manifest);
|
|
13696
|
-
let files;
|
|
13697
|
-
if (opts.files) {
|
|
13698
|
-
files = await glob([opts.files], { nodir: true, ignore: ["**/*.min.css"] });
|
|
13699
|
-
} else {
|
|
13700
|
-
const relStyles = await resolveActiveStyles(manifestDir, resolved.styles);
|
|
13701
|
-
const projectFiles = relStyles.map((rel) => resolve8(manifestDir, rel));
|
|
13702
|
-
let pluginStylePaths = opts.pluginStylePaths;
|
|
13703
|
-
if (pluginStylePaths === undefined) {
|
|
13704
|
-
({ pluginStylePaths } = await loadPluginsWithCss(resolved.extensions, manifestDir, (ref, err) => log.warn(`Skipping plugin "${ref}" for lint — ${err.message}`)));
|
|
13705
|
-
}
|
|
13706
|
-
files = [...projectFiles, ...pluginStylePaths].filter((f) => !f.endsWith(".min.css"));
|
|
13707
|
-
}
|
|
13708
|
-
if (files.length === 0) {
|
|
13709
|
-
log.warn("No CSS files found to lint");
|
|
13710
|
-
return { ok: true, riskyCount: 0, containmentCount: 0, filesLinted: 0 };
|
|
13711
|
-
}
|
|
13712
|
-
log.info(`Linting ${files.length} CSS file(s)`);
|
|
13713
|
-
let errorCount = 0;
|
|
13714
|
-
let riskyCount = 0;
|
|
13715
|
-
let containmentCount = 0;
|
|
13716
|
-
let linted = 0;
|
|
13717
|
-
for (const file of files) {
|
|
13718
|
-
let css;
|
|
13719
|
-
try {
|
|
13720
|
-
css = await readFile31(file, "utf8");
|
|
13721
|
-
} catch (err) {
|
|
13722
|
-
log.error(` ${file}`);
|
|
13723
|
-
log.error(` cannot read stylesheet: ${err instanceof Error ? err.message : String(err)}`);
|
|
13724
|
-
errorCount++;
|
|
13725
|
-
continue;
|
|
13726
|
-
}
|
|
13727
|
-
linted++;
|
|
13728
|
-
const warnings = checkCss(css, file);
|
|
13729
|
-
const errors = warnings.filter((w) => w.severity === "error");
|
|
13730
|
-
const risky = warnings.filter((w) => w.rule === ruleRiskyProps);
|
|
13731
|
-
const containment = warnings.filter((w) => w.rule === rulePageContainment);
|
|
13732
|
-
riskyCount += risky.length;
|
|
13733
|
-
containmentCount += containment.length;
|
|
13734
|
-
if (errors.length > 0) {
|
|
13735
|
-
log.error(` ${file}`);
|
|
13736
|
-
for (const w of errors) {
|
|
13737
|
-
log.error(` ${w.line}:${w.column} ${w.message} (${w.rule})`);
|
|
13738
|
-
}
|
|
13739
|
-
errorCount += errors.length;
|
|
13740
|
-
}
|
|
13741
|
-
const nonErrorWarnings = [...risky, ...containment].sort((a, b) => a.line - b.line);
|
|
13742
|
-
if (nonErrorWarnings.length > 0) {
|
|
13743
|
-
log.warn(` ${file}`);
|
|
13744
|
-
for (const w of nonErrorWarnings) {
|
|
13745
|
-
log.warn(` ${w.line}:${w.column} ${w.message} (${w.rule})`);
|
|
13746
|
-
}
|
|
13747
|
-
}
|
|
13748
|
-
}
|
|
13749
|
-
if (errorCount > 0) {
|
|
13750
|
-
log.error("CSS lint errors found");
|
|
13751
|
-
return { ok: false, riskyCount, containmentCount, filesLinted: linted };
|
|
13752
|
-
}
|
|
13753
|
-
const totalWarnings = riskyCount + containmentCount;
|
|
13754
|
-
if (totalWarnings > 0) {
|
|
13755
|
-
log.warn(`${totalWarnings} print-safety warning(s): ${riskyCount} risky effect(s), ${containmentCount} page-containment`);
|
|
13756
|
-
} else {
|
|
13757
|
-
log.success("CSS lint passed");
|
|
13758
|
-
}
|
|
13759
|
-
return { ok: true, riskyCount, containmentCount, filesLinted: linted };
|
|
13760
|
-
}
|
|
13761
|
-
export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, loadManifestWithPath, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, addBuiltInStyleSet, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listPublishProviders, publishProviderFor, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential, runLint };
|
|
12902
|
+
export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, loadManifestWithPath, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, addBuiltInStyleSet, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listPublishProviders, publishProviderFor, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
|