@quilted/rollup 0.2.4 → 0.2.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/esm/app.mjs +130 -125
  3. package/build/esm/features/node.mjs +57 -0
  4. package/build/esm/module.mjs +21 -22
  5. package/build/esm/package.mjs +36 -79
  6. package/build/esm/server.mjs +16 -25
  7. package/build/esm/shared/magic-module.mjs +5 -3
  8. package/build/esm/shared/project.mjs +100 -0
  9. package/build/tsconfig.tsbuildinfo +1 -1
  10. package/build/typescript/app.d.ts +23 -18
  11. package/build/typescript/app.d.ts.map +1 -1
  12. package/build/typescript/features/env.d.ts +3 -5
  13. package/build/typescript/features/env.d.ts.map +1 -1
  14. package/build/typescript/features/node.d.ts +4 -0
  15. package/build/typescript/features/node.d.ts.map +1 -0
  16. package/build/typescript/features/request-router.d.ts +2 -2
  17. package/build/typescript/module.d.ts +1 -1
  18. package/build/typescript/module.d.ts.map +1 -1
  19. package/build/typescript/package.d.ts +3 -5
  20. package/build/typescript/package.d.ts.map +1 -1
  21. package/build/typescript/server.d.ts +0 -1
  22. package/build/typescript/server.d.ts.map +1 -1
  23. package/build/typescript/shared/magic-module.d.ts +3 -3
  24. package/build/typescript/shared/magic-module.d.ts.map +1 -1
  25. package/build/typescript/shared/project.d.ts +33 -0
  26. package/build/typescript/shared/project.d.ts.map +1 -0
  27. package/package.json +19 -1
  28. package/source/app.ts +158 -154
  29. package/source/features/node.ts +74 -0
  30. package/source/module.ts +20 -22
  31. package/source/package.ts +37 -109
  32. package/source/server.ts +16 -30
  33. package/source/shared/magic-module.ts +10 -4
  34. package/source/shared/project.ts +150 -0
  35. package/build/esm/shared/package-json.mjs +0 -16
  36. package/build/esm/shared/path.mjs +0 -7
  37. package/source/shared/package-json.ts +0 -20
  38. package/source/shared/path.ts +0 -5
package/source/module.ts CHANGED
@@ -1,14 +1,11 @@
1
- import * as path from 'path';
2
1
  import {type InputPluginOption, type RollupOptions} from 'rollup';
3
- import {glob} from 'glob';
4
2
 
5
- import {resolveRoot} from './shared/path.ts';
3
+ import {Project} from './shared/project.ts';
6
4
  import {
7
5
  RollupNodePluginOptions,
8
6
  getNodePlugins,
9
7
  removeBuildFiles,
10
8
  } from './shared/rollup.ts';
11
- import {loadPackageJSON, type PackageJSON} from './shared/package-json.ts';
12
9
  import {
13
10
  getBrowserGroupTargetDetails,
14
11
  rollupGenerateOptionsForBrowsers,
@@ -63,22 +60,22 @@ export interface ModuleAssetsOptions
63
60
  }
64
61
 
65
62
  export async function quiltModule({
66
- root: rootPath = process.cwd(),
63
+ root = process.cwd(),
67
64
  entry,
68
65
  env,
69
66
  assets,
70
67
  graphql = true,
71
68
  }: ModuleOptions = {}) {
72
- const root = resolveRoot(rootPath);
69
+ const project = Project.load(root);
73
70
  const mode = (typeof env === 'object' ? env?.mode : env) ?? 'production';
74
- const outputDirectory = path.join(root, 'build/assets');
71
+ const outputDirectory = project.resolve('build/assets');
75
72
 
76
73
  const minify = assets?.minify ?? true;
77
74
  const hash = assets?.hash ?? 'async-only';
78
75
  const bundle = assets?.bundle ?? true;
79
76
 
80
77
  const browserGroup = await getBrowserGroupTargetDetails(assets?.targets, {
81
- root,
78
+ root: project.root,
82
79
  });
83
80
  const targetFilenamePart = browserGroup.name ? `.${browserGroup.name}` : '';
84
81
 
@@ -87,34 +84,33 @@ export async function quiltModule({
87
84
  {magicModuleEnv, replaceProcessEnv},
88
85
  {sourceCode},
89
86
  {tsconfigAliases},
87
+ {monorepoPackageAliases},
90
88
  {react},
91
89
  {esnext},
92
90
  nodePlugins,
93
- packageJSON,
94
91
  ] = await Promise.all([
95
92
  import('rollup-plugin-visualizer'),
96
93
  import('./features/env.ts'),
97
94
  import('./features/source-code.ts'),
98
95
  import('./features/typescript.ts'),
96
+ import('./features/node.ts'),
99
97
  import('./features/react.ts'),
100
98
  import('./features/esnext.ts'),
101
99
  getNodePlugins({bundle}),
102
- loadPackageJSON(root),
103
100
  ]);
104
101
 
105
- const finalEntry = entry
106
- ? path.resolve(root, entry)
107
- : await sourceForModule(root, packageJSON);
102
+ const finalEntry = await resolveModuleEntry(entry, project);
108
103
 
109
104
  const plugins: InputPluginOption[] = [
110
105
  ...nodePlugins,
111
106
  replaceProcessEnv({mode}),
112
107
  magicModuleEnv({...resolveEnvOption(env), mode}),
113
108
  sourceCode({mode, targets: browserGroup.browsers}),
114
- tsconfigAliases({root}),
109
+ tsconfigAliases({root: project.root}),
110
+ monorepoPackageAliases({root: project.root}),
115
111
  esnext({mode, targets: browserGroup.browsers}),
116
112
  react(),
117
- removeBuildFiles(['build/assets', 'build/reports'], {root}),
113
+ removeBuildFiles(['build/assets', 'build/reports'], {root: project.root}),
118
114
  ];
119
115
 
120
116
  if (graphql) {
@@ -132,8 +128,7 @@ export async function quiltModule({
132
128
  template: 'treemap',
133
129
  open: false,
134
130
  brotliSize: true,
135
- filename: path.resolve(
136
- root,
131
+ filename: project.resolve(
137
132
  `build/reports/bundle-visualizer${targetFilenamePart}.html`,
138
133
  ),
139
134
  }),
@@ -161,19 +156,22 @@ export async function quiltModule({
161
156
  } satisfies RollupOptions;
162
157
  }
163
158
 
164
- async function sourceForModule(root: string, packageJSON: PackageJSON) {
165
- const {main, exports} = packageJSON;
159
+ async function resolveModuleEntry(entry: string | undefined, project: Project) {
160
+ if (entry) {
161
+ return project.resolve(entry);
162
+ }
163
+
164
+ const {main, exports} = project.packageJSON.raw;
166
165
 
167
166
  const entryFromPackageJSON = main ?? (exports as any)?.['.'];
168
167
 
169
168
  if (entryFromPackageJSON) {
170
- return path.resolve(root, entryFromPackageJSON);
169
+ return project.resolve(entryFromPackageJSON);
171
170
  }
172
171
 
173
- const possibleSourceFiles = await glob(
172
+ const possibleSourceFiles = await project.glob(
174
173
  '{index,module,entry,input}.{ts,tsx,mjs,js,jsx}',
175
174
  {
176
- cwd: root,
177
175
  nodir: true,
178
176
  absolute: true,
179
177
  },
package/source/package.ts CHANGED
@@ -4,16 +4,14 @@ import {exec as execCommand} from 'child_process';
4
4
  import {promisify} from 'util';
5
5
 
6
6
  import {Plugin, type RollupOptions, type OutputOptions} from 'rollup';
7
- import {glob} from 'glob';
8
7
 
9
8
  import {multiline} from './shared/strings.ts';
10
- import {resolveRoot} from './shared/path.ts';
9
+ import {Project, sourceEntriesForProject} from './shared/project.ts';
11
10
  import {
12
11
  getNodePlugins,
13
12
  removeBuildFiles,
14
13
  type RollupNodePluginOptions,
15
14
  } from './shared/rollup.ts';
16
- import {loadPackageJSON, type PackageJSON} from './shared/package-json.ts';
17
15
  import {
18
16
  getBrowserGroupTargetDetails,
19
17
  rollupGenerateOptionsForBrowsers,
@@ -128,7 +126,7 @@ export interface PackageOptions extends PackageESModuleOptions {
128
126
  }
129
127
 
130
128
  export async function quiltPackage({
131
- root: rootPath = process.cwd(),
129
+ root = process.cwd(),
132
130
  commonjs = false,
133
131
  esnext: explicitESNext,
134
132
  entries,
@@ -138,10 +136,8 @@ export async function quiltPackage({
138
136
  graphql = true,
139
137
  customize,
140
138
  }: PackageOptions = {}) {
141
- const root = resolveRoot(rootPath);
142
- const packageJSON = await loadPackageJSON(root);
143
- const resolvedEntries =
144
- entries ?? (await sourceEntriesForPackage(root, packageJSON));
139
+ const project = Project.load(root);
140
+ const resolvedEntries = entries ?? (await sourceEntriesForProject(project));
145
141
 
146
142
  const includeESNext =
147
143
  explicitESNext ?? Object.keys(resolvedEntries).length > 0;
@@ -173,37 +169,39 @@ export async function quiltPackage({
173
169
  }
174
170
 
175
171
  export async function quiltPackageESModules({
176
- root: rootPath = process.cwd(),
172
+ root = process.cwd(),
177
173
  commonjs = false,
178
174
  entries,
179
175
  executable = {},
180
- react,
176
+ react: useReact,
181
177
  bundle,
182
178
  graphql = true,
183
179
  customize,
184
180
  }: PackageESModuleOptions = {}) {
185
- const root = resolveRoot(rootPath);
186
- const outputDirectory = path.resolve(root, 'build/esm');
181
+ const project = Project.load(root);
182
+ const outputDirectory = project.resolve('build/esm');
187
183
  const hasExecutables = Object.keys(executable).length > 0;
188
184
 
189
- const [{sourceCode}, {esnext}, nodePlugins, packageJSON, browserGroup] =
185
+ const [{sourceCode}, {react}, {esnext}, nodePlugins, browserGroup] =
190
186
  await Promise.all([
191
187
  import('./features/source-code.ts'),
188
+ import('./features/react.ts'),
192
189
  import('./features/esnext.ts'),
193
190
  getNodePlugins({bundle}),
194
- loadPackageJSON(root),
195
- getBrowserGroupTargetDetails({name: 'default'}, {root}),
191
+ getBrowserGroupTargetDetails({name: 'default'}, {root: project.root}),
196
192
  ]);
197
193
 
198
- const resolvedEntries =
199
- entries ?? (await sourceEntriesForPackage(root, packageJSON));
194
+ const resolvedEntries = entries ?? (await sourceEntriesForProject(project));
200
195
  const hasEntries = Object.keys(resolvedEntries).length > 0;
201
196
 
202
- const source = sourceForEntries({...resolvedEntries, ...executable}, {root});
197
+ const source = sourceForEntries(
198
+ {...resolvedEntries, ...executable},
199
+ {root: project.root},
200
+ );
203
201
 
204
202
  const plugins: Plugin[] = [
205
203
  ...nodePlugins,
206
- sourceCode({mode: 'production', react}),
204
+ sourceCode({mode: 'production', react: useReact}),
207
205
  esnext({mode: 'production', babel: false}),
208
206
  removeBuildFiles(
209
207
  [
@@ -211,12 +209,16 @@ export async function quiltPackageESModules({
211
209
  ...(commonjs ? ['build/cjs'] : []),
212
210
  ...(hasExecutables ? ['bin'] : []),
213
211
  ],
214
- {root},
212
+ {root: project.root},
215
213
  ),
216
214
  ];
217
215
 
216
+ if (useReact) {
217
+ plugins.push(react());
218
+ }
219
+
218
220
  if (hasExecutables) {
219
- plugins.push(packageExecutables(executable, {root}));
221
+ plugins.push(packageExecutables(executable, {root: project.root}));
220
222
  }
221
223
 
222
224
  if (graphql) {
@@ -261,18 +263,6 @@ export async function quiltPackageESModules({
261
263
  const options = {
262
264
  input: source.files,
263
265
  plugins,
264
- onwarn(warning, defaultWarn) {
265
- // Removes annoying warnings for React-focused libraries that
266
- // include 'use client' directives.
267
- if (
268
- warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
269
- /['"]use client['"]/.test(warning.message)
270
- ) {
271
- return;
272
- }
273
-
274
- defaultWarn(warning);
275
- },
276
266
  output,
277
267
  } satisfies RollupOptions;
278
268
 
@@ -319,35 +309,38 @@ export async function quiltPackageESModules({
319
309
  * ```
320
310
  */
321
311
  export async function quiltPackageESNext({
322
- root: rootPath = process.cwd(),
323
- react,
312
+ root = process.cwd(),
313
+ react: useReact,
324
314
  graphql = true,
325
315
  entries,
326
316
  bundle,
327
317
  customize,
328
318
  }: PackageBaseOptions = {}) {
329
- const root = resolveRoot(rootPath);
330
- const outputDirectory = path.join(root, 'build/esnext');
319
+ const project = Project.load(root);
320
+ const outputDirectory = project.resolve('build/esnext');
331
321
 
332
- const [{sourceCode}, {esnext}, nodePlugins, packageJSON] = await Promise.all([
322
+ const [{sourceCode}, {react}, {esnext}, nodePlugins] = await Promise.all([
333
323
  import('./features/source-code.ts'),
324
+ import('./features/react.ts'),
334
325
  import('./features/esnext.ts'),
335
326
  getNodePlugins({bundle}),
336
- loadPackageJSON(root),
337
327
  ]);
338
328
 
339
- const resolvedEntries =
340
- entries ?? (await sourceEntriesForPackage(root, packageJSON));
329
+ const resolvedEntries = entries ?? (await sourceEntriesForProject(project));
341
330
 
342
- const source = sourceForEntries(resolvedEntries, {root});
331
+ const source = sourceForEntries(resolvedEntries, {root: project.root});
343
332
 
344
333
  const plugins: Plugin[] = [
345
334
  ...nodePlugins,
346
- sourceCode({mode: 'production', babel: false, react}),
335
+ sourceCode({mode: 'production', babel: false, react: useReact}),
347
336
  esnext({mode: 'production', babel: false}),
348
- removeBuildFiles(['build/esnext'], {root}),
337
+ removeBuildFiles(['build/esnext'], {root: project.root}),
349
338
  ];
350
339
 
340
+ if (useReact) {
341
+ plugins.push(react());
342
+ }
343
+
351
344
  if (graphql) {
352
345
  const {graphql} = await import('./features/graphql.ts');
353
346
  plugins.push(graphql({manifest: false}));
@@ -505,68 +498,3 @@ function sourceForEntries(
505
498
 
506
499
  return {root: sourceRoot, files: sourceEntryFiles};
507
500
  }
508
-
509
- async function sourceEntriesForPackage(root: string, packageJSON: PackageJSON) {
510
- const {main, exports} = packageJSON;
511
-
512
- const entries: Record<string, string> = {};
513
-
514
- if (typeof main === 'string') {
515
- entries['.'] = await resolveTargetFileAsSource(main, root);
516
- }
517
-
518
- if (typeof exports === 'string') {
519
- entries['.'] = await resolveTargetFileAsSource(exports, root);
520
- return entries;
521
- } else if (exports == null || typeof exports !== 'object') {
522
- return entries;
523
- }
524
-
525
- for (const [exportPath, exportCondition] of Object.entries(
526
- exports as Record<string, null | string | Record<string, string>>,
527
- )) {
528
- let targetFile: string | null | undefined = null;
529
-
530
- if (exportCondition == null) continue;
531
-
532
- if (typeof exportCondition === 'string') {
533
- targetFile = exportCondition;
534
- } else {
535
- targetFile ??=
536
- exportCondition['source'] ??
537
- exportCondition['quilt:source'] ??
538
- exportCondition['quilt:esnext'] ??
539
- Object.values(exportCondition).find(
540
- (condition) =>
541
- typeof condition === 'string' && condition.startsWith('./build/'),
542
- );
543
- }
544
-
545
- if (targetFile == null) continue;
546
-
547
- const sourceFile = await resolveTargetFileAsSource(targetFile, root);
548
-
549
- entries[exportPath] = sourceFile;
550
- }
551
-
552
- return entries;
553
- }
554
-
555
- async function resolveTargetFileAsSource(file: string, root: string) {
556
- const sourceFile = file.includes('/build/')
557
- ? (
558
- await glob(
559
- file
560
- .replace(/[/]build[/][^/]+[/]/, '/*/')
561
- .replace(/(\.d\.ts|\.[\w]+)$/, '.*'),
562
- {
563
- cwd: root,
564
- absolute: true,
565
- ignore: [path.resolve(root, file)],
566
- },
567
- )
568
- )[0]!
569
- : path.resolve(root, file);
570
-
571
- return sourceFile;
572
- }
package/source/server.ts CHANGED
@@ -1,14 +1,12 @@
1
- import * as path from 'path';
2
1
  import {type InputPluginOption, type RollupOptions} from 'rollup';
3
- import {glob} from 'glob';
4
2
 
3
+ import {Project} from './shared/project.ts';
5
4
  import {
6
5
  RollupNodePluginOptions,
7
6
  getNodePlugins,
8
7
  removeBuildFiles,
9
8
  } from './shared/rollup.ts';
10
- import {resolveRoot} from './shared/path.ts';
11
- import {loadPackageJSON, type PackageJSON} from './shared/package-json.ts';
9
+
12
10
  import {magicModuleRequestRouterEntry} from './features/request-router.ts';
13
11
  import {resolveEnvOption, type MagicModuleEnvOptions} from './features/env.ts';
14
12
  import {MAGIC_MODULE_ENTRY, MAGIC_MODULE_REQUEST_ROUTER} from './constants.ts';
@@ -119,10 +117,10 @@ export async function quiltServer({
119
117
  port,
120
118
  host,
121
119
  }: ServerOptions = {}) {
122
- const root = resolveRoot(rootPath);
120
+ const project = Project.load(rootPath);
123
121
  const mode =
124
122
  (typeof env === 'object' ? env?.mode : undefined) ?? 'production';
125
- const outputDirectory = path.join(root, 'build/server');
123
+ const outputDirectory = project.resolve('build/server');
126
124
 
127
125
  const minify = output?.minify ?? false;
128
126
  const bundle = output?.bundle;
@@ -134,24 +132,24 @@ export async function quiltServer({
134
132
  {magicModuleEnv, replaceProcessEnv},
135
133
  {sourceCode},
136
134
  {tsconfigAliases},
135
+ {monorepoPackageAliases},
137
136
  {react},
138
137
  {esnext},
139
138
  nodePlugins,
140
- packageJSON,
141
139
  ] = await Promise.all([
142
140
  import('rollup-plugin-visualizer'),
143
141
  import('./features/env.ts'),
144
142
  import('./features/source-code.ts'),
145
143
  import('./features/typescript.ts'),
144
+ import('./features/node.ts'),
146
145
  import('./features/react.ts'),
147
146
  import('./features/esnext.ts'),
148
147
  getNodePlugins({bundle}),
149
- loadPackageJSON(root),
150
148
  ]);
151
149
 
152
150
  const serverEntry = entry
153
- ? path.resolve(root, entry)
154
- : await sourceForServer(root, packageJSON);
151
+ ? project.resolve(entry)
152
+ : await sourceForServer(project);
155
153
 
156
154
  const finalEntry =
157
155
  format === 'request-router'
@@ -163,10 +161,11 @@ export async function quiltServer({
163
161
  replaceProcessEnv({mode}),
164
162
  magicModuleEnv({...resolveEnvOption(env), mode}),
165
163
  sourceCode({mode, targets: ['current node']}),
166
- tsconfigAliases({root}),
164
+ tsconfigAliases({root: project.root}),
165
+ monorepoPackageAliases({root: project.root}),
167
166
  react(),
168
167
  esnext({mode, targets: ['current node']}),
169
- removeBuildFiles(['build/server', 'build/reports'], {root}),
168
+ removeBuildFiles(['build/server', 'build/reports'], {root: project.root}),
170
169
  ];
171
170
 
172
171
  if (format === 'request-router') {
@@ -198,7 +197,7 @@ export async function quiltServer({
198
197
  template: 'treemap',
199
198
  open: false,
200
199
  brotliSize: true,
201
- filename: path.resolve(root, `build/reports/bundle-visualizer.html`),
200
+ filename: project.resolve(`build/reports/bundle-visualizer.html`),
202
201
  }),
203
202
  );
204
203
 
@@ -206,18 +205,6 @@ export async function quiltServer({
206
205
  input:
207
206
  finalEntry === MAGIC_MODULE_ENTRY ? {server: finalEntry} : finalEntry,
208
207
  plugins,
209
- onwarn(warning, defaultWarn) {
210
- // Removes annoying warnings for React-focused libraries that
211
- // include 'use client' directives.
212
- if (
213
- warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
214
- /['"]use client['"]/.test(warning.message)
215
- ) {
216
- return;
217
- }
218
-
219
- defaultWarn(warning);
220
- },
221
208
  output: {
222
209
  format:
223
210
  outputFormat === 'commonjs' || outputFormat === 'cjs' ? 'cjs' : 'esm',
@@ -232,19 +219,18 @@ export async function quiltServer({
232
219
  } satisfies RollupOptions;
233
220
  }
234
221
 
235
- async function sourceForServer(root: string, packageJSON: PackageJSON) {
236
- const {main, exports} = packageJSON;
222
+ async function sourceForServer(project: Project) {
223
+ const {main, exports} = project.packageJSON.raw;
237
224
 
238
225
  const entryFromPackageJSON = main ?? (exports as any)?.['.'];
239
226
 
240
227
  if (entryFromPackageJSON) {
241
- return path.resolve(root, entryFromPackageJSON);
228
+ return project.resolve(entryFromPackageJSON);
242
229
  }
243
230
 
244
- const possibleSourceFiles = await glob(
231
+ const possibleSourceFiles = await project.glob(
245
232
  '{index,server,service,backend,entry,input}.{ts,tsx,mjs,js,jsx}',
246
233
  {
247
- cwd: root,
248
234
  nodir: true,
249
235
  absolute: true,
250
236
  },
@@ -11,24 +11,30 @@ export function createMagicModulePlugin({
11
11
  sideEffects = false,
12
12
  }: {
13
13
  readonly name: string;
14
- readonly alias?: string;
14
+ readonly alias?: string | (() => string | Promise<string>);
15
15
  readonly module: string;
16
16
  readonly sideEffects?: boolean;
17
17
  source?(this: PluginContext): string | Promise<string>;
18
18
  }) {
19
+ const virtualModuleAlias = `${VIRTUAL_MODULE_PREFIX}${module}${VIRTUAL_MODULE_POSTFIX}`;
20
+
19
21
  return {
20
22
  name,
21
- resolveId(id) {
23
+ async resolveId(id) {
22
24
  if (id !== module) return null;
23
25
 
26
+ const resolved =
27
+ (typeof alias === 'function' ? await alias() : alias) ??
28
+ virtualModuleAlias;
29
+
24
30
  return {
25
- id: alias,
31
+ id: resolved,
26
32
  moduleSideEffects: sideEffects ? 'no-treeshake' : undefined,
27
33
  };
28
34
  },
29
35
  load: getSource
30
36
  ? async function load(source) {
31
- if (source !== alias) return null;
37
+ if (source !== virtualModuleAlias) return null;
32
38
 
33
39
  const code = await getSource.call(this);
34
40
 
@@ -0,0 +1,150 @@
1
+ import * as path from 'path';
2
+ import {readFileSync} from 'fs';
3
+ import {fileURLToPath} from 'url';
4
+
5
+ import {glob, type GlobOptions} from 'glob';
6
+
7
+ const PROJECT_CACHE = new Map<string, Project>();
8
+
9
+ export class Project {
10
+ static load(root: string | URL) {
11
+ const resolvedRoot = resolveRoot(root);
12
+
13
+ let project = PROJECT_CACHE.get(resolvedRoot);
14
+
15
+ if (project == null) {
16
+ project = new Project(resolvedRoot);
17
+ PROJECT_CACHE.set(resolvedRoot, project);
18
+ }
19
+
20
+ return project;
21
+ }
22
+
23
+ readonly root: string;
24
+
25
+ get path() {
26
+ return this.root;
27
+ }
28
+
29
+ get name() {
30
+ return this.packageJSON.name;
31
+ }
32
+
33
+ #packageJSON: PackageJSON | undefined;
34
+ get packageJSON() {
35
+ this.#packageJSON ??= new PackageJSON(this.root);
36
+ return this.#packageJSON;
37
+ }
38
+
39
+ constructor(root: string | URL) {
40
+ this.root = resolveRoot(root);
41
+ }
42
+
43
+ resolve(...segments: string[]) {
44
+ return path.resolve(this.root, ...segments);
45
+ }
46
+
47
+ glob(pattern: string | string[], options?: GlobOptions) {
48
+ return glob(pattern, {
49
+ ...options,
50
+ absolute: true,
51
+ cwd: this.root,
52
+ }) as Promise<string[]>;
53
+ }
54
+ }
55
+
56
+ export interface PackageJSONRaw {
57
+ name: string;
58
+ main?: string;
59
+ private?: boolean;
60
+ exports?: Record<string, string | Record<string, string>>;
61
+ dependencies?: Record<string, string>;
62
+ peerDependencies?: Record<string, string>;
63
+ peerDependenciesMeta?: Record<string, {optional?: boolean}>;
64
+ devDependencies?: Record<string, string>;
65
+ [key: string]: unknown;
66
+ }
67
+
68
+ export class PackageJSON {
69
+ readonly raw: PackageJSONRaw;
70
+ readonly path: string;
71
+
72
+ get name() {
73
+ return this.raw.name;
74
+ }
75
+
76
+ constructor(rootOrPath: string) {
77
+ this.path = rootOrPath.endsWith('package.json')
78
+ ? rootOrPath
79
+ : path.join(rootOrPath, 'package.json');
80
+ this.raw = JSON.parse(readFileSync(this.path, 'utf8'));
81
+ }
82
+ }
83
+
84
+ function resolveRoot(root: string | URL) {
85
+ return typeof root === 'string' ? root : fileURLToPath(root);
86
+ }
87
+
88
+ export async function sourceEntriesForProject(project: Project) {
89
+ const {main, exports} = project.packageJSON.raw;
90
+
91
+ const entries: Record<string, string> = {};
92
+
93
+ if (typeof main === 'string') {
94
+ entries['.'] = await resolveTargetFileAsSource(main, project);
95
+ }
96
+
97
+ if (typeof exports === 'string') {
98
+ entries['.'] = await resolveTargetFileAsSource(exports, project);
99
+ return entries;
100
+ } else if (exports == null || typeof exports !== 'object') {
101
+ return entries;
102
+ }
103
+
104
+ for (const [exportPath, exportCondition] of Object.entries(
105
+ exports as Record<string, null | string | Record<string, string>>,
106
+ )) {
107
+ let targetFile: string | null | undefined = null;
108
+
109
+ if (exportCondition == null) continue;
110
+
111
+ if (typeof exportCondition === 'string') {
112
+ targetFile = exportCondition;
113
+ } else {
114
+ targetFile ??=
115
+ exportCondition['source'] ??
116
+ exportCondition['quilt:source'] ??
117
+ exportCondition['quilt:esnext'] ??
118
+ Object.values(exportCondition).find(
119
+ (condition) =>
120
+ typeof condition === 'string' && condition.startsWith('./build/'),
121
+ );
122
+ }
123
+
124
+ if (targetFile == null) continue;
125
+
126
+ const sourceFile = await resolveTargetFileAsSource(targetFile, project);
127
+
128
+ entries[exportPath] = sourceFile;
129
+ }
130
+
131
+ return entries;
132
+ }
133
+
134
+ async function resolveTargetFileAsSource(file: string, project: Project) {
135
+ const sourceFile = file.includes('/build/')
136
+ ? (
137
+ await project.glob(
138
+ file
139
+ .replace(/[/]build[/][^/]+[/]/, '/*/')
140
+ .replace(/(\.d\.ts|\.[\w]+)$/, '.*'),
141
+ {
142
+ absolute: true,
143
+ ignore: [project.resolve(file)],
144
+ },
145
+ )
146
+ )[0]!
147
+ : project.resolve(file);
148
+
149
+ return sourceFile;
150
+ }
@@ -1,16 +0,0 @@
1
- import * as path from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import { readFile } from 'node:fs/promises';
4
-
5
- async function loadPackageJSON(root) {
6
- const file = await readFile(
7
- path.join(
8
- typeof root === "string" ? root : fileURLToPath(root),
9
- "package.json"
10
- ),
11
- "utf8"
12
- );
13
- return JSON.parse(file);
14
- }
15
-
16
- export { loadPackageJSON };