@remix-run/assets 0.6.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.
Files changed (72) hide show
  1. package/README.md +101 -13
  2. package/dist/assets.d.ts +2 -1
  3. package/dist/assets.d.ts.map +1 -1
  4. package/dist/lib/asset-server.d.ts +41 -19
  5. package/dist/lib/asset-server.d.ts.map +1 -1
  6. package/dist/lib/asset-server.js +98 -41
  7. package/dist/lib/compilation-error.d.ts +1 -1
  8. package/dist/lib/compilation-error.d.ts.map +1 -1
  9. package/dist/lib/files/compiler.d.ts +1 -1
  10. package/dist/lib/files/compiler.d.ts.map +1 -1
  11. package/dist/lib/files/compiler.js +26 -29
  12. package/dist/lib/files/config.d.ts +6 -0
  13. package/dist/lib/files/config.d.ts.map +1 -1
  14. package/dist/lib/files/config.js +9 -0
  15. package/dist/lib/fingerprint.d.ts +0 -4
  16. package/dist/lib/fingerprint.d.ts.map +1 -1
  17. package/dist/lib/fingerprint.js +0 -4
  18. package/dist/lib/hmr.d.ts +7 -0
  19. package/dist/lib/hmr.d.ts.map +1 -1
  20. package/dist/lib/hmr.js +198 -25
  21. package/dist/lib/routes.d.ts +1 -0
  22. package/dist/lib/routes.d.ts.map +1 -1
  23. package/dist/lib/routes.js +1 -1
  24. package/dist/lib/scripts/compiler.d.ts +8 -2
  25. package/dist/lib/scripts/compiler.d.ts.map +1 -1
  26. package/dist/lib/scripts/compiler.js +186 -36
  27. package/dist/lib/scripts/emit.d.ts +2 -1
  28. package/dist/lib/scripts/emit.d.ts.map +1 -1
  29. package/dist/lib/scripts/emit.js +25 -16
  30. package/dist/lib/scripts/resolve.d.ts +7 -1
  31. package/dist/lib/scripts/resolve.d.ts.map +1 -1
  32. package/dist/lib/scripts/resolve.js +110 -3
  33. package/dist/lib/scripts/specifiers.d.ts +2 -0
  34. package/dist/lib/scripts/specifiers.d.ts.map +1 -0
  35. package/dist/lib/scripts/specifiers.js +9 -0
  36. package/dist/lib/scripts/transform.d.ts +2 -3
  37. package/dist/lib/scripts/transform.d.ts.map +1 -1
  38. package/dist/lib/scripts/transform.js +2 -16
  39. package/dist/lib/styles/compiler.d.ts +0 -1
  40. package/dist/lib/styles/compiler.d.ts.map +1 -1
  41. package/dist/lib/styles/compiler.js +105 -25
  42. package/dist/lib/styles/emit.d.ts +2 -1
  43. package/dist/lib/styles/emit.d.ts.map +1 -1
  44. package/dist/lib/styles/emit.js +12 -10
  45. package/dist/lib/styles/resolve.d.ts +0 -1
  46. package/dist/lib/styles/resolve.d.ts.map +1 -1
  47. package/dist/lib/styles/resolve.js +0 -1
  48. package/dist/lib/styles/transform.d.ts +0 -2
  49. package/dist/lib/styles/transform.d.ts.map +1 -1
  50. package/dist/lib/styles/transform.js +0 -7
  51. package/dist/lib/virtual-store.d.ts +8 -0
  52. package/dist/lib/virtual-store.d.ts.map +1 -0
  53. package/dist/lib/virtual-store.js +100 -0
  54. package/package.json +6 -5
  55. package/src/assets.ts +7 -1
  56. package/src/lib/asset-server.ts +175 -73
  57. package/src/lib/compilation-error.ts +1 -0
  58. package/src/lib/files/compiler.ts +30 -40
  59. package/src/lib/files/config.ts +17 -0
  60. package/src/lib/fingerprint.ts +0 -9
  61. package/src/lib/hmr.ts +210 -26
  62. package/src/lib/routes.ts +1 -1
  63. package/src/lib/scripts/compiler.ts +238 -52
  64. package/src/lib/scripts/emit.ts +34 -19
  65. package/src/lib/scripts/resolve.ts +166 -4
  66. package/src/lib/scripts/specifiers.ts +11 -0
  67. package/src/lib/scripts/transform.ts +4 -23
  68. package/src/lib/styles/compiler.ts +137 -39
  69. package/src/lib/styles/emit.ts +18 -13
  70. package/src/lib/styles/resolve.ts +0 -2
  71. package/src/lib/styles/transform.ts +0 -10
  72. package/src/lib/virtual-store.ts +124 -0
@@ -4,7 +4,7 @@ import {
4
4
  createAssetServerCompilationError,
5
5
  isAssetServerCompilationError,
6
6
  } from '../compilation-error.ts'
7
- import { hashContent } from '../fingerprint.ts'
7
+ import { formatFingerprintedPathname, hashContent } from '../fingerprint.ts'
8
8
  import { composeSourceMaps } from '../source-maps.ts'
9
9
  import type { AssetServerCompilationError } from '../compilation-error.ts'
10
10
  import type { ResolvedStyle } from './resolve.ts'
@@ -12,12 +12,12 @@ import type { ResolvedStyle } from './resolve.ts'
12
12
  export type EmittedAsset = {
13
13
  content: string
14
14
  etag: string
15
+ fingerprint: string
15
16
  }
16
17
 
17
18
  export type EmittedStyle = {
18
19
  code: EmittedAsset
19
20
  fingerprint: string | null
20
- importUrls: string[]
21
21
  sourceMap: EmittedAsset | null
22
22
  }
23
23
 
@@ -34,6 +34,7 @@ type EmitResult =
34
34
  export async function emitResolvedStyle(
35
35
  resolvedStyle: ResolvedStyle,
36
36
  options: {
37
+ fingerprintAssets: boolean
37
38
  getServedFileUrl?(
38
39
  identityPath: string,
39
40
  options: {
@@ -45,30 +46,32 @@ export async function emitResolvedStyle(
45
46
  },
46
47
  ): Promise<EmitResult> {
47
48
  try {
48
- let importUrls = await Promise.all(
49
- resolvedStyle.deps.map((depPath) => options.getServedUrl(depPath)),
50
- )
51
49
  let rewriteResult = await rewriteDependencies(resolvedStyle, options)
52
50
  let finalCode = rewriteResult.code
51
+ let sourceMap = rewriteResult.sourceMap
52
+ ? await createEmittedAsset(rewriteResult.sourceMap)
53
+ : null
53
54
 
54
55
  if (rewriteResult.sourceMap) {
55
56
  if (options.sourceMaps === 'inline') {
56
57
  let encoded = Buffer.from(rewriteResult.sourceMap).toString('base64')
57
58
  finalCode += `\n/*# sourceMappingURL=data:application/json;base64,${encoded} */`
58
59
  } else if (options.sourceMaps === 'external') {
59
- finalCode += `\n/*# sourceMappingURL=${await options.getServedUrl(resolvedStyle.identityPath)}.map */`
60
+ finalCode += `\n/*# sourceMappingURL=${formatFingerprintedPathname(
61
+ resolvedStyle.stableUrlPathname,
62
+ options.fingerprintAssets && sourceMap ? sourceMap.fingerprint : null,
63
+ )}.map */`
60
64
  }
61
65
  }
62
66
 
67
+ let code = await createEmittedAsset(finalCode)
68
+
63
69
  return {
64
70
  ok: true,
65
71
  value: {
66
- code: await createEmittedAsset(finalCode),
67
- fingerprint: resolvedStyle.fingerprint,
68
- importUrls,
69
- sourceMap: rewriteResult.sourceMap
70
- ? await createEmittedAsset(rewriteResult.sourceMap)
71
- : null,
72
+ code,
73
+ fingerprint: options.fingerprintAssets ? code.fingerprint : null,
74
+ sourceMap,
72
75
  },
73
76
  }
74
77
  } catch (error) {
@@ -192,9 +195,11 @@ async function getServedFileUrl(
192
195
  }
193
196
 
194
197
  async function createEmittedAsset(content: string): Promise<EmittedAsset> {
198
+ let fingerprint = await hashContent(content)
195
199
  return {
196
200
  content,
197
- etag: `W/"${await hashContent(content)}"`,
201
+ etag: `W/"${fingerprint}"`,
202
+ fingerprint,
198
203
  }
199
204
  }
200
205
 
@@ -38,7 +38,6 @@ type ResolvedDependency =
38
38
  export type ResolvedStyle = {
39
39
  dependencies: ResolvedDependency[]
40
40
  deps: string[]
41
- fingerprint: string | null
42
41
  identityPath: string
43
42
  rawCode: string
44
43
  resolvedPath: string
@@ -131,7 +130,6 @@ export async function resolveStyle(
131
130
  value: {
132
131
  dependencies,
133
132
  deps: [...deps],
134
- fingerprint: transformed.fingerprint,
135
133
  identityPath: record.identityPath,
136
134
  rawCode: transformed.rawCode,
137
135
  resolvedPath: transformed.resolvedPath,
@@ -5,7 +5,6 @@ import {
5
5
  createAssetServerCompilationError,
6
6
  isAssetServerCompilationError,
7
7
  } from '../compilation-error.ts'
8
- import { generateFingerprint } from '../fingerprint.ts'
9
8
  import type { ModuleTracking } from '../module-store.ts'
10
9
  import { rewriteSourceMapSources, stringifySourceMap } from '../source-maps.ts'
11
10
  import type { AssetServerCompilationError } from '../compilation-error.ts'
@@ -25,7 +24,6 @@ type TransformedStyleDependency =
25
24
  }
26
25
 
27
26
  export type TransformedStyle = {
28
- fingerprint: string | null
29
27
  identityPath: string
30
28
  rawCode: string
31
29
  resolvedPath: string
@@ -49,7 +47,6 @@ type TransformResult = {
49
47
  )
50
48
 
51
49
  export type TransformArgs = {
52
- buildId: string | null
53
50
  isWatchIgnored(filePath: string): boolean
54
51
  minify: boolean
55
52
  routes: CompiledRoutes
@@ -149,13 +146,6 @@ export async function transformStyle(
149
146
  trackedFiles,
150
147
  },
151
148
  value: {
152
- fingerprint:
153
- args.buildId === null
154
- ? null
155
- : await generateFingerprint({
156
- buildId: args.buildId,
157
- content: sourceText,
158
- }),
159
149
  identityPath: record.identityPath,
160
150
  rawCode: Buffer.from(transformResult.code).toString('utf8'),
161
151
  resolvedPath,
@@ -0,0 +1,124 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+
4
+ import {
5
+ getFilePathBaseName,
6
+ getFilePathDirectory,
7
+ normalizeFilePath,
8
+ resolveFilePath,
9
+ } from './paths.ts'
10
+ import { resolveMountFileRoot } from './routes.ts'
11
+
12
+ const modulesStateFileName = '.modules.yaml'
13
+ const virtualStoreBasePath = '/__@remix/virtual-store'
14
+ const virtualStoreDirLineRE = /^virtualStoreDir:[ \t]*(.+)$/m
15
+
16
+ // pnpm's global virtual store links packages from a machine-wide directory, so package realpaths
17
+ // land outside `rootDir` and would otherwise have no public URL. The store location comes from
18
+ // `node_modules/.modules.yaml`, which pnpm rewrites on every install.
19
+ export function getVirtualStoreMountConfigs(options: {
20
+ mounts: Readonly<Record<string, string>>
21
+ rootDir: string
22
+ }): {
23
+ mounts: Record<string, string>
24
+ rootDir: string
25
+ }[] {
26
+ let virtualStoreDir = findVirtualStoreDir(options.rootDir)
27
+ if (virtualStoreDir === null) return []
28
+ // A store inside a mount, such as pnpm's default `node_modules/.pnpm`, already has URLs.
29
+ if (isWithinMounts(virtualStoreDir, options)) return []
30
+
31
+ return [
32
+ {
33
+ mounts: {
34
+ [virtualStoreBasePath]: getFilePathBaseName(virtualStoreDir),
35
+ },
36
+ rootDir: getFilePathDirectory(virtualStoreDir),
37
+ },
38
+ ]
39
+ }
40
+
41
+ function findVirtualStoreDir(rootDir: string): string | null {
42
+ let directory = normalizeFilePath(rootDir)
43
+
44
+ // Workspace members share the workspace root's file, so walk up to the nearest one.
45
+ while (true) {
46
+ let modulesDir = `${directory}/node_modules`
47
+ let modulesState = readModulesState(`${modulesDir}/${modulesStateFileName}`)
48
+ if (modulesState !== null) {
49
+ let virtualStoreDir = readVirtualStoreDir(modulesState)
50
+ if (virtualStoreDir === null) return null
51
+ return realpathDirectory(resolveFilePath(modulesDir, virtualStoreDir))
52
+ }
53
+
54
+ let parentDirectory = normalizeFilePath(path.dirname(directory))
55
+ if (parentDirectory === directory) return null
56
+ directory = parentDirectory
57
+ }
58
+ }
59
+
60
+ function readVirtualStoreDir(modulesState: string): string | null {
61
+ // pnpm 10.34 and later write JSON, earlier versions write YAML, and package managers that
62
+ // mirror the global virtual store write either one. Reading the one scalar this needs keeps
63
+ // both formats working without a YAML parser.
64
+ try {
65
+ let parsedState = JSON.parse(modulesState) as unknown
66
+ if (parsedState === null || typeof parsedState !== 'object') return null
67
+
68
+ let virtualStoreDir = (parsedState as Record<string, unknown>).virtualStoreDir
69
+ return typeof virtualStoreDir === 'string' ? virtualStoreDir : null
70
+ } catch {
71
+ let match = virtualStoreDirLineRE.exec(modulesState)
72
+ return match === null ? null : unquoteScalar(match[1].trim())
73
+ }
74
+ }
75
+
76
+ function unquoteScalar(value: string): string {
77
+ let quote = value[0]
78
+ if (value.length > 1 && (quote === "'" || quote === '"') && value.endsWith(quote)) {
79
+ return value.slice(1, -1)
80
+ }
81
+
82
+ return value
83
+ }
84
+
85
+ function isWithinMounts(
86
+ directory: string,
87
+ options: {
88
+ mounts: Readonly<Record<string, string>>
89
+ rootDir: string
90
+ },
91
+ ): boolean {
92
+ return Object.values(options.mounts).some((fileRoot) => {
93
+ let mountRoot = resolveMountFileRoot(options.rootDir, fileRoot)
94
+ return directory === mountRoot || directory.startsWith(`${mountRoot}/`)
95
+ })
96
+ }
97
+
98
+ function readModulesState(filePath: string): string | null {
99
+ try {
100
+ return fs.readFileSync(filePath, 'utf-8')
101
+ } catch (error) {
102
+ if (isPathNotFoundError(error)) return null
103
+ throw error
104
+ }
105
+ }
106
+
107
+ function realpathDirectory(filePath: string): string | null {
108
+ try {
109
+ let realPath = fs.realpathSync(filePath)
110
+ return fs.statSync(realPath).isDirectory() ? normalizeFilePath(realPath) : null
111
+ } catch (error) {
112
+ if (isPathNotFoundError(error)) return null
113
+ throw error
114
+ }
115
+ }
116
+
117
+ function isPathNotFoundError(error: unknown): boolean {
118
+ return (
119
+ error instanceof Error &&
120
+ 'code' in error &&
121
+ ((error as NodeJS.ErrnoException).code === 'ENOENT' ||
122
+ (error as NodeJS.ErrnoException).code === 'ENOTDIR')
123
+ )
124
+ }