@remix-run/assets 0.5.0 → 0.7.0
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 +185 -104
- package/dist/assets.d.ts +4 -1
- package/dist/assets.d.ts.map +1 -1
- package/dist/lib/access.d.ts +29 -2
- package/dist/lib/access.d.ts.map +1 -1
- package/dist/lib/access.js +52 -26
- package/dist/lib/asset-server.d.ts +61 -25
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +127 -55
- package/dist/lib/compilation-error.d.ts +1 -1
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/files/compiler.d.ts +1 -1
- package/dist/lib/files/compiler.d.ts.map +1 -1
- package/dist/lib/files/compiler.js +28 -31
- package/dist/lib/files/config.d.ts +6 -0
- package/dist/lib/files/config.d.ts.map +1 -1
- package/dist/lib/files/config.js +9 -0
- package/dist/lib/fingerprint.d.ts +0 -4
- package/dist/lib/fingerprint.d.ts.map +1 -1
- package/dist/lib/fingerprint.js +0 -4
- package/dist/lib/hmr.d.ts +7 -0
- package/dist/lib/hmr.d.ts.map +1 -1
- package/dist/lib/hmr.js +198 -25
- package/dist/lib/injected-packages.d.ts +3 -2
- package/dist/lib/injected-packages.d.ts.map +1 -1
- package/dist/lib/injected-packages.js +11 -8
- package/dist/lib/inspection.d.ts +39 -0
- package/dist/lib/inspection.d.ts.map +1 -0
- package/dist/lib/inspection.js +160 -0
- package/dist/lib/routes.d.ts +12 -3
- package/dist/lib/routes.d.ts.map +1 -1
- package/dist/lib/routes.js +124 -80
- package/dist/lib/scripts/compiler.d.ts +8 -2
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +188 -38
- package/dist/lib/scripts/emit.d.ts +2 -1
- package/dist/lib/scripts/emit.d.ts.map +1 -1
- package/dist/lib/scripts/emit.js +25 -16
- package/dist/lib/scripts/resolve.d.ts +7 -1
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +119 -12
- package/dist/lib/scripts/specifiers.d.ts +2 -0
- package/dist/lib/scripts/specifiers.d.ts.map +1 -0
- package/dist/lib/scripts/specifiers.js +9 -0
- package/dist/lib/scripts/transform.d.ts +2 -3
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +4 -18
- package/dist/lib/styles/compiler.d.ts +0 -1
- package/dist/lib/styles/compiler.d.ts.map +1 -1
- package/dist/lib/styles/compiler.js +105 -25
- package/dist/lib/styles/emit.d.ts +2 -1
- package/dist/lib/styles/emit.d.ts.map +1 -1
- package/dist/lib/styles/emit.js +12 -10
- package/dist/lib/styles/resolve.d.ts +0 -1
- package/dist/lib/styles/resolve.d.ts.map +1 -1
- package/dist/lib/styles/resolve.js +8 -9
- package/dist/lib/styles/transform.d.ts +0 -2
- package/dist/lib/styles/transform.d.ts.map +1 -1
- package/dist/lib/styles/transform.js +2 -9
- package/dist/lib/virtual-store.d.ts +8 -0
- package/dist/lib/virtual-store.d.ts.map +1 -0
- package/dist/lib/virtual-store.js +100 -0
- package/package.json +6 -6
- package/src/assets.ts +9 -1
- package/src/lib/access.ts +83 -30
- package/src/lib/asset-server.ts +224 -94
- package/src/lib/compilation-error.ts +4 -3
- package/src/lib/files/compiler.ts +32 -42
- package/src/lib/files/config.ts +17 -0
- package/src/lib/fingerprint.ts +0 -9
- package/src/lib/hmr.ts +210 -26
- package/src/lib/injected-packages.ts +16 -10
- package/src/lib/inspection.ts +229 -0
- package/src/lib/routes.ts +158 -126
- package/src/lib/scripts/compiler.ts +240 -54
- package/src/lib/scripts/emit.ts +34 -19
- package/src/lib/scripts/resolve.ts +175 -13
- package/src/lib/scripts/specifiers.ts +11 -0
- package/src/lib/scripts/transform.ts +6 -25
- package/src/lib/styles/compiler.ts +137 -39
- package/src/lib/styles/emit.ts +18 -13
- package/src/lib/styles/resolve.ts +8 -10
- package/src/lib/styles/transform.ts +2 -12
- package/src/lib/virtual-store.ts +124 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/lib/styles/resolve.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAA;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,KAAK,WAAW,GAAG,YAAY,CAAC,gBAAgB,EAAE,aAAa,EAAE,YAAY,CAAC,CAAA;AAE9E,KAAK,kBAAkB,GACnB;IACE,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,GACD;IACE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IAC1C,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAEL,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,EAAE,kBAAkB,EAAE,CAAA;IAClC,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/lib/styles/resolve.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAA;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,KAAK,WAAW,GAAG,YAAY,CAAC,gBAAgB,EAAE,aAAa,EAAE,YAAY,CAAC,CAAA;AAE9E,KAAK,kBAAkB,GACnB;IACE,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,GACD;IACE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;IAC1C,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAEL,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,EAAE,kBAAkB,EAAE,CAAA;IAClC,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3C,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACzC,MAAM,EAAE,cAAc,CAAA;CACvB,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,QAAQ,EAAE,cAAc,CAAA;CACzB,GAAG,CACA;IACE,EAAE,EAAE,IAAI,CAAA;IACR,KAAK,EAAE,aAAa,CAAA;CACrB,GACD;IACE,KAAK,EAAE,2BAA2B,CAAA;IAClC,EAAE,EAAE,KAAK,CAAA;CACV,CACJ,CAAA;AAQD,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,gBAAgB,EAC7B,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,aAAa,CAAC,CA8DxB;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,GAChB;IACD,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAmCA"}
|
|
@@ -40,7 +40,6 @@ export async function resolveStyle(record, transformed, args) {
|
|
|
40
40
|
value: {
|
|
41
41
|
dependencies,
|
|
42
42
|
deps: [...deps],
|
|
43
|
-
fingerprint: transformed.fingerprint,
|
|
44
43
|
identityPath: record.identityPath,
|
|
45
44
|
rawCode: transformed.rawCode,
|
|
46
45
|
resolvedPath: transformed.resolvedPath,
|
|
@@ -70,8 +69,8 @@ export function resolveServedStyleOrThrow(filePath, args) {
|
|
|
70
69
|
}
|
|
71
70
|
let stableUrlPathname = args.routes.toUrlPathname(identityPath);
|
|
72
71
|
if (!stableUrlPathname) {
|
|
73
|
-
throw createAssetServerCompilationError(`File ${identityPath} is outside all configured
|
|
74
|
-
code: '
|
|
72
|
+
throw createAssetServerCompilationError(`File ${identityPath} is outside all configured mounts.`, {
|
|
73
|
+
code: 'FILE_OUTSIDE_MOUNTS',
|
|
75
74
|
});
|
|
76
75
|
}
|
|
77
76
|
return { identityPath, stableUrlPathname };
|
|
@@ -107,9 +106,9 @@ function resolveImportDependency(url, importerPath, placeholder, args) {
|
|
|
107
106
|
});
|
|
108
107
|
}
|
|
109
108
|
if (!args.routes.toUrlPathname(identityPath)) {
|
|
110
|
-
throw createAssetServerCompilationError(`Import "${url}" in ${importerPath}, resolved to "${identityPath}", is outside all configured
|
|
111
|
-
`Add a matching
|
|
112
|
-
code: '
|
|
109
|
+
throw createAssetServerCompilationError(`Import "${url}" in ${importerPath}, resolved to "${identityPath}", is outside all configured mounts. ` +
|
|
110
|
+
`Add a matching mount for this file path, or mark this import as external.`, {
|
|
111
|
+
code: 'IMPORT_OUTSIDE_MOUNTS',
|
|
113
112
|
});
|
|
114
113
|
}
|
|
115
114
|
return {
|
|
@@ -156,9 +155,9 @@ function resolveUrlDependency(url, importerPath, placeholder, args) {
|
|
|
156
155
|
});
|
|
157
156
|
}
|
|
158
157
|
if (!args.routes.toUrlPathname(identityPath)) {
|
|
159
|
-
throw createAssetServerCompilationError(`URL "${url}" in ${importerPath}, resolved to "${identityPath}", is outside all configured
|
|
160
|
-
`Add a matching
|
|
161
|
-
code: '
|
|
158
|
+
throw createAssetServerCompilationError(`URL "${url}" in ${importerPath}, resolved to "${identityPath}", is outside all configured mounts. ` +
|
|
159
|
+
`Add a matching mount for this file path.`, {
|
|
160
|
+
code: 'URL_OUTSIDE_MOUNTS',
|
|
162
161
|
});
|
|
163
162
|
}
|
|
164
163
|
let parsedRequest = parseResolvedFileRequest({ hash, search });
|
|
@@ -12,7 +12,6 @@ type TransformedStyleDependency = {
|
|
|
12
12
|
url: string;
|
|
13
13
|
};
|
|
14
14
|
export type TransformedStyle = {
|
|
15
|
-
fingerprint: string | null;
|
|
16
15
|
identityPath: string;
|
|
17
16
|
rawCode: string;
|
|
18
17
|
resolvedPath: string;
|
|
@@ -31,7 +30,6 @@ type TransformResult = {
|
|
|
31
30
|
ok: false;
|
|
32
31
|
});
|
|
33
32
|
export type TransformArgs = {
|
|
34
|
-
buildId: string | null;
|
|
35
33
|
isWatchIgnored(filePath: string): boolean;
|
|
36
34
|
minify: boolean;
|
|
37
35
|
routes: CompiledRoutes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/lib/styles/transform.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/lib/styles/transform.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAA;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvD,KAAK,0BAA0B,GAC3B;IACE,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;CACZ,GACD;IACE,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,KAAK,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAEL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,sBAAsB,EAAE,0BAA0B,EAAE,CAAA;CACrD,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,cAAc,CAAA;CACzB,GAAG,CACA;IACE,EAAE,EAAE,IAAI,CAAA;IACR,KAAK,EAAE,gBAAgB,CAAA;CACxB,GACD;IACE,KAAK,EAAE,2BAA2B,CAAA;IAClC,EAAE,EAAE,KAAK,CAAA;CACV,CACJ,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACzC,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAA;IACxC,oBAAoB,EAAE,UAAU,GAAG,KAAK,CAAA;IACxC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;CACpC,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,eAAe,CAAC,CAsG1B"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
2
|
import { transform } from 'lightningcss';
|
|
3
3
|
import { createAssetServerCompilationError, isAssetServerCompilationError, } from '../compilation-error.js';
|
|
4
|
-
import { generateFingerprint } from '../fingerprint.js';
|
|
5
4
|
import { rewriteSourceMapSources, stringifySourceMap } from '../source-maps.js';
|
|
6
5
|
export async function transformStyle(record, args) {
|
|
7
6
|
let resolvedPath = record.identityPath;
|
|
@@ -34,8 +33,8 @@ export async function transformStyle(record, args) {
|
|
|
34
33
|
try {
|
|
35
34
|
let stableUrlPathname = args.routes.toUrlPathname(record.identityPath);
|
|
36
35
|
if (!stableUrlPathname) {
|
|
37
|
-
throw createAssetServerCompilationError(`File ${record.identityPath} is outside all configured
|
|
38
|
-
code: '
|
|
36
|
+
throw createAssetServerCompilationError(`File ${record.identityPath} is outside all configured mounts.`, {
|
|
37
|
+
code: 'FILE_OUTSIDE_MOUNTS',
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
40
|
let transformResult = runLightningTransform(resolvedPath, rawBytes, {
|
|
@@ -72,12 +71,6 @@ export async function transformStyle(record, args) {
|
|
|
72
71
|
trackedFiles,
|
|
73
72
|
},
|
|
74
73
|
value: {
|
|
75
|
-
fingerprint: args.buildId === null
|
|
76
|
-
? null
|
|
77
|
-
: await generateFingerprint({
|
|
78
|
-
buildId: args.buildId,
|
|
79
|
-
content: sourceText,
|
|
80
|
-
}),
|
|
81
74
|
identityPath: record.identityPath,
|
|
82
75
|
rawCode: Buffer.from(transformResult.code).toString('utf8'),
|
|
83
76
|
resolvedPath,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-store.d.ts","sourceRoot":"","sources":["../../src/lib/virtual-store.ts"],"names":[],"mappings":"AAkBA,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;CAChB,EAAE,CAcF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { getFilePathBaseName, getFilePathDirectory, normalizeFilePath, resolveFilePath, } from './paths.js';
|
|
4
|
+
import { resolveMountFileRoot } from './routes.js';
|
|
5
|
+
const modulesStateFileName = '.modules.yaml';
|
|
6
|
+
const virtualStoreBasePath = '/__@remix/virtual-store';
|
|
7
|
+
const virtualStoreDirLineRE = /^virtualStoreDir:[ \t]*(.+)$/m;
|
|
8
|
+
// pnpm's global virtual store links packages from a machine-wide directory, so package realpaths
|
|
9
|
+
// land outside `rootDir` and would otherwise have no public URL. The store location comes from
|
|
10
|
+
// `node_modules/.modules.yaml`, which pnpm rewrites on every install.
|
|
11
|
+
export function getVirtualStoreMountConfigs(options) {
|
|
12
|
+
let virtualStoreDir = findVirtualStoreDir(options.rootDir);
|
|
13
|
+
if (virtualStoreDir === null)
|
|
14
|
+
return [];
|
|
15
|
+
// A store inside a mount, such as pnpm's default `node_modules/.pnpm`, already has URLs.
|
|
16
|
+
if (isWithinMounts(virtualStoreDir, options))
|
|
17
|
+
return [];
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
mounts: {
|
|
21
|
+
[virtualStoreBasePath]: getFilePathBaseName(virtualStoreDir),
|
|
22
|
+
},
|
|
23
|
+
rootDir: getFilePathDirectory(virtualStoreDir),
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
function findVirtualStoreDir(rootDir) {
|
|
28
|
+
let directory = normalizeFilePath(rootDir);
|
|
29
|
+
// Workspace members share the workspace root's file, so walk up to the nearest one.
|
|
30
|
+
while (true) {
|
|
31
|
+
let modulesDir = `${directory}/node_modules`;
|
|
32
|
+
let modulesState = readModulesState(`${modulesDir}/${modulesStateFileName}`);
|
|
33
|
+
if (modulesState !== null) {
|
|
34
|
+
let virtualStoreDir = readVirtualStoreDir(modulesState);
|
|
35
|
+
if (virtualStoreDir === null)
|
|
36
|
+
return null;
|
|
37
|
+
return realpathDirectory(resolveFilePath(modulesDir, virtualStoreDir));
|
|
38
|
+
}
|
|
39
|
+
let parentDirectory = normalizeFilePath(path.dirname(directory));
|
|
40
|
+
if (parentDirectory === directory)
|
|
41
|
+
return null;
|
|
42
|
+
directory = parentDirectory;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function readVirtualStoreDir(modulesState) {
|
|
46
|
+
// pnpm 10.34 and later write JSON, earlier versions write YAML, and package managers that
|
|
47
|
+
// mirror the global virtual store write either one. Reading the one scalar this needs keeps
|
|
48
|
+
// both formats working without a YAML parser.
|
|
49
|
+
try {
|
|
50
|
+
let parsedState = JSON.parse(modulesState);
|
|
51
|
+
if (parsedState === null || typeof parsedState !== 'object')
|
|
52
|
+
return null;
|
|
53
|
+
let virtualStoreDir = parsedState.virtualStoreDir;
|
|
54
|
+
return typeof virtualStoreDir === 'string' ? virtualStoreDir : null;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
let match = virtualStoreDirLineRE.exec(modulesState);
|
|
58
|
+
return match === null ? null : unquoteScalar(match[1].trim());
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function unquoteScalar(value) {
|
|
62
|
+
let quote = value[0];
|
|
63
|
+
if (value.length > 1 && (quote === "'" || quote === '"') && value.endsWith(quote)) {
|
|
64
|
+
return value.slice(1, -1);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
function isWithinMounts(directory, options) {
|
|
69
|
+
return Object.values(options.mounts).some((fileRoot) => {
|
|
70
|
+
let mountRoot = resolveMountFileRoot(options.rootDir, fileRoot);
|
|
71
|
+
return directory === mountRoot || directory.startsWith(`${mountRoot}/`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function readModulesState(filePath) {
|
|
75
|
+
try {
|
|
76
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (isPathNotFoundError(error))
|
|
80
|
+
return null;
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function realpathDirectory(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
let realPath = fs.realpathSync(filePath);
|
|
87
|
+
return fs.statSync(realPath).isDirectory() ? normalizeFilePath(realPath) : null;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (isPathNotFoundError(error))
|
|
91
|
+
return null;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function isPathNotFoundError(error) {
|
|
96
|
+
return (error instanceof Error &&
|
|
97
|
+
'code' in error &&
|
|
98
|
+
(error.code === 'ENOENT' ||
|
|
99
|
+
error.code === 'ENOTDIR'));
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/assets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Fetch-based server for compiling browser JS/TS and CSS assets on demand",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,20 +41,20 @@
|
|
|
41
41
|
"oxc-transform": "^0.121.0",
|
|
42
42
|
"picomatch": "^4.0.4",
|
|
43
43
|
"source-map-js": "^1.2.1",
|
|
44
|
-
"@remix-run/file-storage": "^0.13.7",
|
|
45
|
-
"@remix-run/headers": "^0.21.1",
|
|
46
44
|
"@remix-run/mime": "^0.4.2",
|
|
47
|
-
"@remix-run/
|
|
45
|
+
"@remix-run/file-storage": "^0.13.7",
|
|
46
|
+
"@remix-run/headers": "^0.21.1"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
50
49
|
"@types/node": "^24.6.0",
|
|
51
50
|
"@types/picomatch": "^4.0.3",
|
|
51
|
+
"playwright": "^1.60.0",
|
|
52
52
|
"typescript": "^7.0.2",
|
|
53
53
|
"@remix-run/assert": "0.3.0",
|
|
54
|
-
"@remix-run/node-hmr": "^0.1.0",
|
|
55
54
|
"@remix-run/node-tsx": "^0.1.1",
|
|
55
|
+
"@remix-run/node-hmr": "^0.2.0",
|
|
56
56
|
"@remix-run/test": "0.6.0",
|
|
57
|
-
"@remix-run/ui": "0.
|
|
57
|
+
"@remix-run/ui": "0.9.0"
|
|
58
58
|
},
|
|
59
59
|
"keywords": [
|
|
60
60
|
"remix",
|
package/src/assets.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export { createAssetServer } from './lib/asset-server.ts'
|
|
2
2
|
export { defineFileTransform } from './lib/files/config.ts'
|
|
3
|
-
export type {
|
|
3
|
+
export type { AssetAccessDetails, AssetAccessRule } from './lib/access.ts'
|
|
4
|
+
export type {
|
|
5
|
+
AssetServer,
|
|
6
|
+
AssetServerOptions,
|
|
7
|
+
BrowserHmrChannel,
|
|
8
|
+
ScriptEntry,
|
|
9
|
+
} from './lib/asset-server.ts'
|
|
10
|
+
export type { AssetDetails, AssetKind, AssetStatus } from './lib/inspection.ts'
|
|
11
|
+
export type { ScriptImportMap } from './lib/scripts/compiler.ts'
|
|
4
12
|
export type { ModuleLoader } from './lib/loaders.ts'
|
package/src/lib/access.ts
CHANGED
|
@@ -4,9 +4,30 @@ import { createFileMatcher } from './file-matcher.ts'
|
|
|
4
4
|
import { isInjectedPackageFilePath } from './injected-packages.ts'
|
|
5
5
|
import { normalizeFilePath } from './paths.ts'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/** Access-policy result for an inspected asset file. */
|
|
8
|
+
export interface AssetAccessDetails {
|
|
9
|
+
/** Whether the asset server may serve the file. */
|
|
10
|
+
allowed: boolean
|
|
11
|
+
/** The first configured rule that allowed the file, when one matched. */
|
|
12
|
+
allowedBy?: AssetAccessRule
|
|
13
|
+
/** The first matching `denyFiles` pattern, when access was denied. */
|
|
14
|
+
deniedBy?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Rule that allows an inspected asset file to be served. */
|
|
18
|
+
export type AssetAccessRule =
|
|
19
|
+
/** A matching `allowFiles` entry. */
|
|
20
|
+
| { kind: 'file'; value: string }
|
|
21
|
+
/** A runtime file provided internally by the asset server. */
|
|
22
|
+
| { kind: 'injected'; value: string }
|
|
23
|
+
/** A matching `allowPackages` entry. */
|
|
24
|
+
| { kind: 'package'; value: string }
|
|
25
|
+
|
|
26
|
+
export type AccessPolicy = {
|
|
27
|
+
getAllowedPackageRoots(): readonly string[]
|
|
8
28
|
getPackageWatchDirectories(): readonly string[]
|
|
9
29
|
handleFileEvent(filePath: string): void
|
|
30
|
+
inspect(filePath: string): AssetAccessDetails
|
|
10
31
|
isAllowed(filePath: string): boolean
|
|
11
32
|
}
|
|
12
33
|
|
|
@@ -29,13 +50,15 @@ export function createAccessPolicy(options: {
|
|
|
29
50
|
packageSearchRoots?: readonly string[]
|
|
30
51
|
rootDir: string
|
|
31
52
|
}): AccessPolicy {
|
|
32
|
-
let allowMatchers = options.allowFiles.map((pattern) =>
|
|
33
|
-
createFileMatcher(pattern, options.rootDir),
|
|
34
|
-
|
|
53
|
+
let allowMatchers = options.allowFiles.map((pattern) => ({
|
|
54
|
+
matcher: createFileMatcher(pattern, options.rootDir),
|
|
55
|
+
pattern,
|
|
56
|
+
}))
|
|
35
57
|
let allowPackageNames = normalizePackageNames(options.allowPackages, 'allowPackages')
|
|
36
|
-
let denyMatchers = (options.denyFiles ?? []).map((pattern) =>
|
|
37
|
-
createFileMatcher(pattern, options.rootDir),
|
|
38
|
-
|
|
58
|
+
let denyMatchers = (options.denyFiles ?? []).map((pattern) => ({
|
|
59
|
+
matcher: createFileMatcher(pattern, options.rootDir),
|
|
60
|
+
pattern,
|
|
61
|
+
}))
|
|
39
62
|
let packageSearchRoots = [options.rootDir, ...(options.packageSearchRoots ?? [])]
|
|
40
63
|
let packageRootPaths = createPackageRootPaths({
|
|
41
64
|
allowPackageNames,
|
|
@@ -57,14 +80,44 @@ export function createAccessPolicy(options: {
|
|
|
57
80
|
packageRootsDirty = false
|
|
58
81
|
}
|
|
59
82
|
|
|
60
|
-
function
|
|
61
|
-
if (allowPackageNames.size === 0) return
|
|
83
|
+
function getAllowedPackageName(filePath: string): string | undefined {
|
|
84
|
+
if (allowPackageNames.size === 0) return undefined
|
|
62
85
|
refreshPackageRootPathTries()
|
|
63
86
|
|
|
64
|
-
return
|
|
87
|
+
return getPackageNameFromRootPathTrie(filePath, allowPackageRootPathTrie)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function inspect(filePath: string): AssetAccessDetails {
|
|
91
|
+
if (isInjectedPackageFilePath(filePath)) {
|
|
92
|
+
return { allowed: true, allowedBy: { kind: 'injected', value: '@remix-run/assets' } }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let allowedBy: AssetAccessRule | undefined
|
|
96
|
+
let allowMatch = allowMatchers.find(({ matcher }) => matcher(filePath))
|
|
97
|
+
if (allowMatch) {
|
|
98
|
+
allowedBy = { kind: 'file', value: allowMatch.pattern }
|
|
99
|
+
} else {
|
|
100
|
+
let packageName = getAllowedPackageName(filePath)
|
|
101
|
+
if (packageName !== undefined) {
|
|
102
|
+
allowedBy = { kind: 'package', value: packageName }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!allowedBy) return { allowed: false }
|
|
107
|
+
|
|
108
|
+
let denyMatch = denyMatchers.find(({ matcher }) => matcher(filePath))
|
|
109
|
+
if (denyMatch) {
|
|
110
|
+
return { allowed: false, allowedBy, deniedBy: denyMatch.pattern }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { allowed: true, allowedBy }
|
|
65
114
|
}
|
|
66
115
|
|
|
67
116
|
return {
|
|
117
|
+
getAllowedPackageRoots() {
|
|
118
|
+
refreshPackageRootPathTries()
|
|
119
|
+
return [...packageRootPaths.keys()]
|
|
120
|
+
},
|
|
68
121
|
getPackageWatchDirectories() {
|
|
69
122
|
if (allowPackageNames.size === 0) return []
|
|
70
123
|
return packageStateDirectories
|
|
@@ -75,13 +128,9 @@ export function createAccessPolicy(options: {
|
|
|
75
128
|
|
|
76
129
|
packageRootsDirty = true
|
|
77
130
|
},
|
|
131
|
+
inspect,
|
|
78
132
|
isAllowed(filePath) {
|
|
79
|
-
|
|
80
|
-
if (!allowMatchers.some((matcher) => matcher(filePath)) && !isAllowedPackage(filePath)) {
|
|
81
|
-
return false
|
|
82
|
-
}
|
|
83
|
-
if (denyMatchers.length > 0 && denyMatchers.some((matcher) => matcher(filePath))) return false
|
|
84
|
-
return true
|
|
133
|
+
return inspect(filePath).allowed
|
|
85
134
|
},
|
|
86
135
|
}
|
|
87
136
|
}
|
|
@@ -121,7 +170,7 @@ type PackageJson = {
|
|
|
121
170
|
|
|
122
171
|
type PackageRootPathTrie = {
|
|
123
172
|
children: Map<string, PackageRootPathTrie>
|
|
124
|
-
|
|
173
|
+
packageName?: string
|
|
125
174
|
}
|
|
126
175
|
|
|
127
176
|
type PackageRootQueueItem = {
|
|
@@ -132,8 +181,8 @@ type PackageRootQueueItem = {
|
|
|
132
181
|
function createPackageRootPaths(options: {
|
|
133
182
|
allowPackageNames: ReadonlySet<string>
|
|
134
183
|
searchRoots: readonly string[]
|
|
135
|
-
}):
|
|
136
|
-
let allowPackageRootPaths = new
|
|
184
|
+
}): Map<string, string> {
|
|
185
|
+
let allowPackageRootPaths = new Map<string, string>()
|
|
137
186
|
let allowQueue: PackageRootQueueItem[] = []
|
|
138
187
|
let seenAllowedPackageRoots = new Set<string>()
|
|
139
188
|
let searchRoots = normalizePackageSearchRoots(options.searchRoots)
|
|
@@ -155,13 +204,13 @@ function createPackageRootPaths(options: {
|
|
|
155
204
|
}
|
|
156
205
|
|
|
157
206
|
while (allowQueue.length > 0) {
|
|
158
|
-
let { packageJsonPath } = allowQueue.shift()!
|
|
207
|
+
let { packageJsonPath, packageName } = allowQueue.shift()!
|
|
159
208
|
let packageRootPath = normalizeFilePath(path.dirname(packageJsonPath))
|
|
160
209
|
if (seenAllowedPackageRoots.has(packageRootPath)) continue
|
|
161
210
|
seenAllowedPackageRoots.add(packageRootPath)
|
|
162
211
|
|
|
163
212
|
let packageJson = readPackageJson(packageJsonPath)
|
|
164
|
-
allowPackageRootPaths.
|
|
213
|
+
allowPackageRootPaths.set(packageRootPath, packageName)
|
|
165
214
|
|
|
166
215
|
for (let dependencyName of Object.keys(packageJson.dependencies ?? {})) {
|
|
167
216
|
validatePackageName(
|
|
@@ -197,10 +246,12 @@ function createPackageRootPaths(options: {
|
|
|
197
246
|
return allowPackageRootPaths
|
|
198
247
|
}
|
|
199
248
|
|
|
200
|
-
function createPackageRootPathTrie(
|
|
249
|
+
function createPackageRootPathTrie(
|
|
250
|
+
packageRootPaths: ReadonlyMap<string, string>,
|
|
251
|
+
): PackageRootPathTrie {
|
|
201
252
|
let rootNode = createPackageRootPathTrieNode()
|
|
202
253
|
|
|
203
|
-
for (let packageRootPath of packageRootPaths) {
|
|
254
|
+
for (let [packageRootPath, packageName] of packageRootPaths) {
|
|
204
255
|
let node = rootNode
|
|
205
256
|
for (let segment of getFilePathSegments(packageRootPath)) {
|
|
206
257
|
let childNode = node.children.get(segment)
|
|
@@ -210,7 +261,7 @@ function createPackageRootPathTrie(packageRootPaths: ReadonlySet<string>): Packa
|
|
|
210
261
|
}
|
|
211
262
|
node = childNode
|
|
212
263
|
}
|
|
213
|
-
node.
|
|
264
|
+
node.packageName = packageName
|
|
214
265
|
}
|
|
215
266
|
|
|
216
267
|
return rootNode
|
|
@@ -219,22 +270,24 @@ function createPackageRootPathTrie(packageRootPaths: ReadonlySet<string>): Packa
|
|
|
219
270
|
function createPackageRootPathTrieNode(): PackageRootPathTrie {
|
|
220
271
|
return {
|
|
221
272
|
children: new Map(),
|
|
222
|
-
packageRoot: false,
|
|
223
273
|
}
|
|
224
274
|
}
|
|
225
275
|
|
|
226
|
-
function
|
|
276
|
+
function getPackageNameFromRootPathTrie(
|
|
277
|
+
filePath: string,
|
|
278
|
+
trie: PackageRootPathTrie,
|
|
279
|
+
): string | undefined {
|
|
227
280
|
let node = trie
|
|
228
|
-
if (node.
|
|
281
|
+
if (node.packageName !== undefined) return node.packageName
|
|
229
282
|
|
|
230
283
|
for (let segment of getFilePathSegments(filePath)) {
|
|
231
284
|
let childNode = node.children.get(segment)
|
|
232
|
-
if (!childNode) return
|
|
233
|
-
if (childNode.
|
|
285
|
+
if (!childNode) return undefined
|
|
286
|
+
if (childNode.packageName !== undefined) return childNode.packageName
|
|
234
287
|
node = childNode
|
|
235
288
|
}
|
|
236
289
|
|
|
237
|
-
return
|
|
290
|
+
return undefined
|
|
238
291
|
}
|
|
239
292
|
|
|
240
293
|
function getFilePathSegments(filePath: string): string[] {
|