1
1
import path from "path" ;
2
+ import crypto from "crypto" ;
2
3
import {
3
4
ComponentDirectory ,
4
5
ComponentDefinitionPath ,
5
6
buildComponentDirectory ,
6
7
isComponentDirectory ,
7
8
qualifiedDefinitionPath ,
8
9
toComponentDefinitionPath ,
10
+ EncodedComponentDefinitionPath ,
9
11
} from "./directoryStructure.js" ;
10
12
import {
11
13
Context ,
@@ -15,7 +17,6 @@ import {
15
17
} from "../../../../bundler/context.js" ;
16
18
import esbuild , { BuildOptions , Metafile , OutputFile , Plugin } from "esbuild" ;
17
19
import chalk from "chalk" ;
18
- import { createRequire } from "module" ;
19
20
import {
20
21
AppDefinitionSpecWithoutImpls ,
21
22
ComponentDefinitionSpecWithoutImpls ,
@@ -93,22 +94,15 @@ function componentPlugin({
93
94
}
94
95
let resolvedPath = undefined ;
95
96
for ( const candidate of candidates ) {
96
- try {
97
- // --experimental- import-meta-resolve is required for
98
- // `import.meta.resolve` so we'll use `require.resolve`
99
- // until then. Hopefully they aren't too different.
100
- const require = createRequire ( args . resolveDir ) ;
101
- resolvedPath = require . resolve ( candidate , {
102
- paths : [ args . resolveDir ] ,
103
- } ) ;
97
+ const result = await build . resolve ( candidate , {
98
+ // We expect this to be " import-statement" but pass 'kind' through
99
+ // to say honest to normal esbuild behavior.
100
+ kind : args . kind ,
101
+ resolveDir : args . resolveDir ,
102
+ } ) ;
103
+ if ( result . path ) {
104
+ resolvedPath = result . path ;
104
105
break ;
105
- } catch ( e : any ) {
106
- if ( e . code === "MODULE_NOT_FOUND" ) {
107
- continue ;
108
- }
109
- // We always invoke esbuild in a try/catch.
110
- // eslint-disable-next-line no-restricted-syntax
111
- throw e ;
112
106
}
113
107
}
114
108
if ( resolvedPath === undefined ) {
@@ -129,7 +123,12 @@ function componentPlugin({
129
123
}
130
124
131
125
verbose &&
132
- logMessage ( ctx , " -> Component import! Recording it." , args . path ) ;
126
+ logMessage (
127
+ ctx ,
128
+ " -> Component import! Recording it." ,
129
+ args . path ,
130
+ resolvedPath ,
131
+ ) ;
133
132
134
133
if ( mode === "discover" ) {
135
134
return {
@@ -146,7 +145,7 @@ function componentPlugin({
146
145
rootComponentDirectory ,
147
146
imported ,
148
147
) ;
149
- const encodedPath = hackyMapping ( componentPath ) ;
148
+ const encodedPath = hackyMapping ( encodeDefinitionPath ( componentPath ) ) ;
150
149
return {
151
150
path : encodedPath ,
152
151
external : true ,
@@ -158,7 +157,7 @@ function componentPlugin({
158
157
}
159
158
160
159
/** The path on the deployment that identifier a component definition. */
161
- function hackyMapping ( componentPath : ComponentDefinitionPath ) : string {
160
+ function hackyMapping ( componentPath : EncodedComponentDefinitionPath ) : string {
162
161
return `./_componentDeps/${ Buffer . from ( componentPath ) . toString ( "base64" ) . replace ( / = + $ / , "" ) } ` ;
163
162
}
164
163
@@ -312,6 +311,24 @@ async function findComponentDependencies(
312
311
return { components, dependencyGraph } ;
313
312
}
314
313
314
+ // Each path component is less than 64 bytes and escape all a-zA-Z0-9
315
+ // This is the only version of the path the server will receive.
316
+ export function encodeDefinitionPath (
317
+ s : ComponentDefinitionPath ,
318
+ ) : EncodedComponentDefinitionPath {
319
+ const components = s . split ( path . sep ) ;
320
+ return components
321
+ . map ( ( s ) => {
322
+ const escaped = s . replaceAll ( "-" , "_" ) . replaceAll ( "+" , "_" ) ;
323
+ if ( escaped . length <= 64 ) {
324
+ return escaped ;
325
+ }
326
+ const hash = crypto . createHash ( "md5" ) . update ( s ) . digest ( "hex" ) ;
327
+ return `${ escaped . slice ( 0 , 50 ) } ${ hash . slice ( 0 , 14 ) } ` ;
328
+ } )
329
+ . join ( path . sep ) as EncodedComponentDefinitionPath ;
330
+ }
331
+
315
332
// NB: If a directory linked to is not a member of the passed
316
333
// componentDirectories array then there will be external links
317
334
// with no corresponding definition bundle.
@@ -414,9 +431,15 @@ export async function bundleDefinitions(
414
431
( out ) => out . directory . path !== rootComponentDirectory . path ,
415
432
) ;
416
433
417
- const componentDefinitionSpecsWithoutImpls = componentBundles . map (
418
- ( { directory, outputJs, outputJsMap } ) => ( {
419
- definitionPath : directory . path ,
434
+ const componentDefinitionSpecsWithoutImpls : ComponentDefinitionSpecWithoutImpls [ ] =
435
+ componentBundles . map ( ( { directory, outputJs, outputJsMap } ) => ( {
436
+ definitionPath : encodeDefinitionPath (
437
+ toComponentDefinitionPath ( rootComponentDirectory , directory ) ,
438
+ ) ,
439
+ origDefinitionPath : toComponentDefinitionPath (
440
+ rootComponentDirectory ,
441
+ directory ,
442
+ ) ,
420
443
definition : {
421
444
path : path . relative ( directory . path , outputJs . path ) ,
422
445
source : outputJs . text ,
@@ -427,15 +450,14 @@ export async function bundleDefinitions(
427
450
rootComponentDirectory ,
428
451
dependencyGraph ,
429
452
directory . definitionPath ,
430
- ) ,
431
- } ) ,
432
- ) ;
453
+ ) . map ( encodeDefinitionPath ) ,
454
+ } ) ) ;
433
455
const appDeps = getDeps (
434
456
rootComponentDirectory ,
435
457
dependencyGraph ,
436
458
appBundle . directory . definitionPath ,
437
- ) ;
438
- const appDefinitionSpecWithoutImpls = {
459
+ ) . map ( encodeDefinitionPath ) ;
460
+ const appDefinitionSpecWithoutImpls : AppDefinitionSpecWithoutImpls = {
439
461
definition : {
440
462
path : path . relative ( rootComponentDirectory . path , appBundle . outputJs . path ) ,
441
463
source : appBundle . outputJs . text ,
@@ -465,7 +487,7 @@ export async function bundleImplementations(
465
487
componentImplementations : {
466
488
schema : Bundle | null ;
467
489
functions : Bundle [ ] ;
468
- definitionPath : ComponentDefinitionPath ;
490
+ definitionPath : EncodedComponentDefinitionPath ;
469
491
} [ ] ;
470
492
} > {
471
493
let appImplementation ;
@@ -478,10 +500,12 @@ export async function bundleImplementations(
478
500
directory . path ,
479
501
) ;
480
502
let schema ;
481
- if ( ! ctx . fs . exists ( path . resolve ( resolvedPath , "schema.ts" ) ) ) {
482
- schema = null ;
483
- } else {
503
+ if ( ctx . fs . exists ( path . resolve ( resolvedPath , "schema.ts" ) ) ) {
504
+ schema = ( await bundleSchema ( ctx , resolvedPath ) ) [ 0 ] || null ;
505
+ } else if ( ctx . fs . exists ( path . resolve ( resolvedPath , "schema.js" ) ) ) {
484
506
schema = ( await bundleSchema ( ctx , resolvedPath ) ) [ 0 ] || null ;
507
+ } else {
508
+ schema = null ;
485
509
}
486
510
487
511
const entryPoints = await entryPointsByEnvironment (
@@ -538,15 +562,11 @@ export async function bundleImplementations(
538
562
externalNodeDependencies,
539
563
} ;
540
564
} else {
541
- componentImplementations . push ( {
542
- // these needs to be a componentPath when sent to the server
543
- definitionPath : toComponentDefinitionPath (
544
- rootComponentDirectory ,
545
- directory ,
546
- ) ,
547
- schema,
548
- functions,
549
- } ) ;
565
+ // definitionPath is the canonical form
566
+ const definitionPath = encodeDefinitionPath (
567
+ toComponentDefinitionPath ( rootComponentDirectory , directory ) ,
568
+ ) ;
569
+ componentImplementations . push ( { definitionPath, schema, functions } ) ;
550
570
}
551
571
isRoot = false ;
552
572
}
0 commit comments