@normed/bundle 4.8.3 → 4.8.4
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/bundles/bin/cli.js +132 -25
- package/bundles/bin/cli.js.map +3 -3
- package/bundles/esbuild-plugins/load_pug.d.ts +10 -0
- package/bundles/index.js +132 -25
- package/bundles/index.js.map +3 -3
- package/package.json +1 -1
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type * as esbuild from "esbuild";
|
|
2
|
+
export declare function findPackageRoot(filePath: string): string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve an asset path from a pug file, treating "/" as srcWebRoot (not filesystem root).
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveAssetPath(assetPath: string, pugDir: string, srcWebRoot: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Assert that an absolute path is within one of the allowed source roots.
|
|
9
|
+
* Throws if the path escapes all allowed roots.
|
|
10
|
+
*/
|
|
11
|
+
export declare function assertWithinAllowedRoots(absolutePath: string, allowedRoots: string[], originalValue: string, pugFilePath: string): void;
|
|
2
12
|
/**
|
|
3
13
|
* Discovered pug file reference from HTML.
|
|
4
14
|
*/
|
package/bundles/index.js
CHANGED
|
@@ -68878,6 +68878,42 @@ function resolvePackagePath(packageSpecifier, fromFile) {
|
|
|
68878
68878
|
return null;
|
|
68879
68879
|
}
|
|
68880
68880
|
}
|
|
68881
|
+
var packageRootCache = /* @__PURE__ */ new Map();
|
|
68882
|
+
function findPackageRoot(filePath) {
|
|
68883
|
+
let dir = path5.dirname(path5.resolve(filePath));
|
|
68884
|
+
const cacheKey = dir;
|
|
68885
|
+
if (packageRootCache.has(cacheKey)) return packageRootCache.get(cacheKey);
|
|
68886
|
+
while (true) {
|
|
68887
|
+
if (fs6.existsSync(path5.join(dir, "package.json"))) {
|
|
68888
|
+
packageRootCache.set(cacheKey, dir);
|
|
68889
|
+
return dir;
|
|
68890
|
+
}
|
|
68891
|
+
const parent = path5.dirname(dir);
|
|
68892
|
+
if (parent === dir) {
|
|
68893
|
+
packageRootCache.set(cacheKey, null);
|
|
68894
|
+
return null;
|
|
68895
|
+
}
|
|
68896
|
+
dir = parent;
|
|
68897
|
+
}
|
|
68898
|
+
}
|
|
68899
|
+
function resolveAssetPath(assetPath, pugDir, srcWebRoot) {
|
|
68900
|
+
if (assetPath.startsWith("/")) {
|
|
68901
|
+
return path5.resolve(srcWebRoot, assetPath.slice(1));
|
|
68902
|
+
}
|
|
68903
|
+
return path5.resolve(pugDir, assetPath);
|
|
68904
|
+
}
|
|
68905
|
+
function assertWithinAllowedRoots(absolutePath, allowedRoots, originalValue, pugFilePath) {
|
|
68906
|
+
const normalizedPath = path5.resolve(absolutePath);
|
|
68907
|
+
const withinARoot = allowedRoots.some((root) => {
|
|
68908
|
+
const normalizedRoot = path5.resolve(root);
|
|
68909
|
+
return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + path5.sep);
|
|
68910
|
+
});
|
|
68911
|
+
if (!withinARoot) {
|
|
68912
|
+
throw new Error(
|
|
68913
|
+
`Security: asset path "${originalValue}" resolves to "${normalizedPath}" which is outside the allowed source directories [${allowedRoots.join(", ")}]. Referenced in ${pugFilePath}. Use the copy: or build: prefix to resolve via Node module resolution.`
|
|
68914
|
+
);
|
|
68915
|
+
}
|
|
68916
|
+
}
|
|
68881
68917
|
var discoveredPugReferences = /* @__PURE__ */ new Map();
|
|
68882
68918
|
var discoveredLessReferences = /* @__PURE__ */ new Map();
|
|
68883
68919
|
var discoveredScriptReferences = /* @__PURE__ */ new Map();
|
|
@@ -68912,7 +68948,7 @@ function computeHtmlAssetPath(opts) {
|
|
|
68912
68948
|
}
|
|
68913
68949
|
return path5.relative(opts.htmlOutputDir, opts.absoluteOutput);
|
|
68914
68950
|
}
|
|
68915
|
-
async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
68951
|
+
async function processHtmlAssets(html, pugFilePath, options, webRoot, collectedReferences) {
|
|
68916
68952
|
const assets = [];
|
|
68917
68953
|
const pugReferences = [];
|
|
68918
68954
|
const lessReferences = [];
|
|
@@ -68923,30 +68959,43 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
68923
68959
|
const outbase = options.outbase || path5.dirname(pugFilePath);
|
|
68924
68960
|
const pugDir = path5.dirname(pugFilePath);
|
|
68925
68961
|
const publicPath = options.publicPath || "";
|
|
68926
|
-
const
|
|
68927
|
-
const
|
|
68928
|
-
|
|
68962
|
+
const srcWebRoot = webRoot ? path5.join(outbase, webRoot) : outbase;
|
|
68963
|
+
const normalizedOutbase = path5.resolve(outbase);
|
|
68964
|
+
const normalizedPug = path5.resolve(pugFilePath);
|
|
68965
|
+
const pugWithinOutbase = normalizedPug === normalizedOutbase || normalizedPug.startsWith(normalizedOutbase + path5.sep);
|
|
68966
|
+
const packageRoot = pugWithinOutbase ? null : findPackageRoot(pugFilePath);
|
|
68967
|
+
const allowedRoots = [outbase, packageRoot].filter(
|
|
68968
|
+
(r) => r != null
|
|
68929
68969
|
);
|
|
68930
|
-
const
|
|
68970
|
+
const processedAssets = /* @__PURE__ */ new Map();
|
|
68931
68971
|
let modifiedHtml = html;
|
|
68932
68972
|
const matches = [];
|
|
68933
|
-
|
|
68934
|
-
|
|
68935
|
-
|
|
68936
|
-
|
|
68937
|
-
|
|
68938
|
-
|
|
68939
|
-
|
|
68940
|
-
|
|
68941
|
-
|
|
68942
|
-
})
|
|
68973
|
+
if (collectedReferences && collectedReferences.length > 0) {
|
|
68974
|
+
for (const ref of collectedReferences) {
|
|
68975
|
+
matches.push({ attr: ref.attr, value: ref.value });
|
|
68976
|
+
}
|
|
68977
|
+
} else {
|
|
68978
|
+
const allAttrs = [...new Set(Object.values(ASSET_ATTRIBUTES).flat())].join(
|
|
68979
|
+
"|"
|
|
68980
|
+
);
|
|
68981
|
+
const attrRegex = new RegExp(
|
|
68982
|
+
`(${allAttrs})\\s*=\\s*["']([^"']+)["']`,
|
|
68983
|
+
"gi"
|
|
68984
|
+
);
|
|
68985
|
+
let match;
|
|
68986
|
+
while ((match = attrRegex.exec(html)) !== null) {
|
|
68987
|
+
const attr = match[1];
|
|
68988
|
+
const value = match[2];
|
|
68989
|
+
if (attr && value) {
|
|
68990
|
+
matches.push({ attr: attr.toLowerCase(), value });
|
|
68991
|
+
}
|
|
68943
68992
|
}
|
|
68944
68993
|
}
|
|
68945
68994
|
const discoveredPugPaths = /* @__PURE__ */ new Set();
|
|
68946
68995
|
const discoveredLessPaths = /* @__PURE__ */ new Set();
|
|
68947
68996
|
const discoveredScriptPaths = /* @__PURE__ */ new Set();
|
|
68948
68997
|
const discoveredPackageCssPaths = /* @__PURE__ */ new Set();
|
|
68949
|
-
for (const {
|
|
68998
|
+
for (const { attr, value } of matches) {
|
|
68950
68999
|
let newValue = value;
|
|
68951
69000
|
if (attr === "srcset") {
|
|
68952
69001
|
const srcsetParts = value.split(",");
|
|
@@ -68959,7 +69008,13 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
68959
69008
|
[getVerbatimPath(assetPath), ...descriptors].filter(Boolean).join(" ")
|
|
68960
69009
|
);
|
|
68961
69010
|
} else if (assetPath && isPugReference(assetPath)) {
|
|
68962
|
-
const absolutePath =
|
|
69011
|
+
const absolutePath = resolveAssetPath(assetPath, pugDir, srcWebRoot);
|
|
69012
|
+
assertWithinAllowedRoots(
|
|
69013
|
+
absolutePath,
|
|
69014
|
+
allowedRoots,
|
|
69015
|
+
assetPath,
|
|
69016
|
+
pugFilePath
|
|
69017
|
+
);
|
|
68963
69018
|
if (!discoveredPugPaths.has(absolutePath)) {
|
|
68964
69019
|
discoveredPugPaths.add(absolutePath);
|
|
68965
69020
|
pugReferences.push({ originalHref: assetPath, absolutePath });
|
|
@@ -68976,6 +69031,8 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
68976
69031
|
publicPath,
|
|
68977
69032
|
assets,
|
|
68978
69033
|
processedAssets,
|
|
69034
|
+
srcWebRoot,
|
|
69035
|
+
allowedRoots,
|
|
68979
69036
|
webRoot
|
|
68980
69037
|
);
|
|
68981
69038
|
if (hashedPath) {
|
|
@@ -68993,19 +69050,22 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
68993
69050
|
} else if (isVerbatimReference(value)) {
|
|
68994
69051
|
newValue = getVerbatimPath(value);
|
|
68995
69052
|
} else if (isPugReference(value)) {
|
|
68996
|
-
const absolutePath =
|
|
69053
|
+
const absolutePath = resolveAssetPath(value, pugDir, srcWebRoot);
|
|
69054
|
+
assertWithinAllowedRoots(absolutePath, allowedRoots, value, pugFilePath);
|
|
68997
69055
|
if (!discoveredPugPaths.has(absolutePath)) {
|
|
68998
69056
|
discoveredPugPaths.add(absolutePath);
|
|
68999
69057
|
pugReferences.push({ originalHref: value, absolutePath });
|
|
69000
69058
|
}
|
|
69001
69059
|
} else if (isLessReference(value)) {
|
|
69002
|
-
const absolutePath =
|
|
69060
|
+
const absolutePath = resolveAssetPath(value, pugDir, srcWebRoot);
|
|
69061
|
+
assertWithinAllowedRoots(absolutePath, allowedRoots, value, pugFilePath);
|
|
69003
69062
|
if (!discoveredLessPaths.has(absolutePath)) {
|
|
69004
69063
|
discoveredLessPaths.add(absolutePath);
|
|
69005
69064
|
lessReferences.push({ originalHref: value, absolutePath });
|
|
69006
69065
|
}
|
|
69007
69066
|
} else if (isScriptReference(value)) {
|
|
69008
|
-
const absolutePath =
|
|
69067
|
+
const absolutePath = resolveAssetPath(value, pugDir, srcWebRoot);
|
|
69068
|
+
assertWithinAllowedRoots(absolutePath, allowedRoots, value, pugFilePath);
|
|
69009
69069
|
if (!discoveredScriptPaths.has(absolutePath)) {
|
|
69010
69070
|
discoveredScriptPaths.add(absolutePath);
|
|
69011
69071
|
scriptReferences.push({ originalHref: value, absolutePath });
|
|
@@ -69137,6 +69197,8 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
69137
69197
|
publicPath,
|
|
69138
69198
|
assets,
|
|
69139
69199
|
processedAssets,
|
|
69200
|
+
srcWebRoot,
|
|
69201
|
+
allowedRoots,
|
|
69140
69202
|
webRoot
|
|
69141
69203
|
);
|
|
69142
69204
|
if (hashedPath) {
|
|
@@ -69144,8 +69206,9 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
69144
69206
|
}
|
|
69145
69207
|
}
|
|
69146
69208
|
if (newValue !== value) {
|
|
69147
|
-
const
|
|
69148
|
-
|
|
69209
|
+
const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
69210
|
+
const valueRegex = new RegExp(`(["'])${escapedValue}\\1`);
|
|
69211
|
+
modifiedHtml = modifiedHtml.replace(valueRegex, `$1${newValue}$1`);
|
|
69149
69212
|
}
|
|
69150
69213
|
}
|
|
69151
69214
|
return {
|
|
@@ -69157,8 +69220,14 @@ async function processHtmlAssets(html, pugFilePath, options, webRoot) {
|
|
|
69157
69220
|
packageCssReferences
|
|
69158
69221
|
};
|
|
69159
69222
|
}
|
|
69160
|
-
async function processAsset(assetPath, pugDir, pugFilePath, outdir, outbase, assetNames, publicPath, assets, processedAssets, webRoot) {
|
|
69161
|
-
const absoluteSource =
|
|
69223
|
+
async function processAsset(assetPath, pugDir, pugFilePath, outdir, outbase, assetNames, publicPath, assets, processedAssets, srcWebRoot, allowedRoots, webRoot) {
|
|
69224
|
+
const absoluteSource = resolveAssetPath(assetPath, pugDir, srcWebRoot);
|
|
69225
|
+
assertWithinAllowedRoots(
|
|
69226
|
+
absoluteSource,
|
|
69227
|
+
allowedRoots,
|
|
69228
|
+
assetPath,
|
|
69229
|
+
pugFilePath
|
|
69230
|
+
);
|
|
69162
69231
|
if (processedAssets.has(absoluteSource)) {
|
|
69163
69232
|
return processedAssets.get(absoluteSource);
|
|
69164
69233
|
}
|
|
@@ -69353,6 +69422,36 @@ function createRebaseAssetsPlugin(entryFilePath) {
|
|
|
69353
69422
|
}
|
|
69354
69423
|
};
|
|
69355
69424
|
}
|
|
69425
|
+
function createAssetCollectorPlugin(collectedReferences) {
|
|
69426
|
+
return {
|
|
69427
|
+
preCodeGen(ast) {
|
|
69428
|
+
const pending = [...ast?.nodes || []];
|
|
69429
|
+
while (pending.length > 0) {
|
|
69430
|
+
const node = pending.shift();
|
|
69431
|
+
if (!node) continue;
|
|
69432
|
+
if (node.block?.nodes) {
|
|
69433
|
+
pending.push(...node.block.nodes);
|
|
69434
|
+
}
|
|
69435
|
+
if (node.nodes) {
|
|
69436
|
+
pending.push(...node.nodes);
|
|
69437
|
+
}
|
|
69438
|
+
const assetAttrs = ASSET_ATTRIBUTES[node.name];
|
|
69439
|
+
if (!assetAttrs) continue;
|
|
69440
|
+
if (!node.attrs) continue;
|
|
69441
|
+
for (const attr of node.attrs) {
|
|
69442
|
+
if (!assetAttrs.includes(attr.name)) continue;
|
|
69443
|
+
const extracted = extractStaticString(attr.val);
|
|
69444
|
+
if (!extracted) continue;
|
|
69445
|
+
collectedReferences.push({
|
|
69446
|
+
attr: attr.name,
|
|
69447
|
+
value: extracted.value
|
|
69448
|
+
});
|
|
69449
|
+
}
|
|
69450
|
+
}
|
|
69451
|
+
return ast;
|
|
69452
|
+
}
|
|
69453
|
+
};
|
|
69454
|
+
}
|
|
69356
69455
|
async function loadAsText2(_filepath) {
|
|
69357
69456
|
return {
|
|
69358
69457
|
loader: "text"
|
|
@@ -69394,12 +69493,14 @@ async function loadAsHtml(filepath, options) {
|
|
|
69394
69493
|
}
|
|
69395
69494
|
async function loadAsEntrypoint(filepath, options, webRoot) {
|
|
69396
69495
|
const fileData = await fs6.promises.readFile(filepath, "utf8");
|
|
69496
|
+
const collectedReferences = [];
|
|
69397
69497
|
let contents = import_pug.default.render(fileData, {
|
|
69398
69498
|
filename: filepath,
|
|
69399
69499
|
basedir: options.outbase,
|
|
69400
69500
|
name: "render",
|
|
69401
69501
|
plugins: [
|
|
69402
69502
|
createRebaseAssetsPlugin(filepath),
|
|
69503
|
+
createAssetCollectorPlugin(collectedReferences),
|
|
69403
69504
|
pugTransformer(isScriptNodeWithSrcAttr, (nodes) => {
|
|
69404
69505
|
nodes;
|
|
69405
69506
|
})
|
|
@@ -69413,7 +69514,13 @@ async function loadAsEntrypoint(filepath, options, webRoot) {
|
|
|
69413
69514
|
lessReferences,
|
|
69414
69515
|
scriptReferences,
|
|
69415
69516
|
packageCssReferences
|
|
69416
|
-
} = await processHtmlAssets(
|
|
69517
|
+
} = await processHtmlAssets(
|
|
69518
|
+
contents,
|
|
69519
|
+
filepath,
|
|
69520
|
+
options,
|
|
69521
|
+
webRoot,
|
|
69522
|
+
collectedReferences
|
|
69523
|
+
);
|
|
69417
69524
|
contents = processedHtml;
|
|
69418
69525
|
if (pugReferences.length > 0) {
|
|
69419
69526
|
discoveredPugReferences.set(filepath, pugReferences);
|