jiek 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/bin/jiek.js +13 -0
  4. package/dist/cli.cjs +5041 -0
  5. package/dist/cli.d.cts +112 -0
  6. package/dist/cli.d.ts +112 -0
  7. package/dist/cli.js +5010 -0
  8. package/dist/cli.min.cjs +19 -0
  9. package/dist/cli.min.js +19 -0
  10. package/dist/index.cjs +5 -0
  11. package/dist/index.d.cts +73 -0
  12. package/dist/index.d.ts +73 -0
  13. package/dist/index.js +3 -0
  14. package/dist/index.min.cjs +1 -0
  15. package/dist/index.min.js +1 -0
  16. package/dist/rollup/index.cjs +4688 -0
  17. package/dist/rollup/index.d.cts +53 -0
  18. package/dist/rollup/index.d.ts +53 -0
  19. package/dist/rollup/index.js +4673 -0
  20. package/dist/rollup/index.min.cjs +19 -0
  21. package/dist/rollup/index.min.js +19 -0
  22. package/package.json +89 -4
  23. package/src/cli.ts +9 -0
  24. package/src/commands/base.ts +8 -0
  25. package/src/commands/build.ts +158 -0
  26. package/src/commands/init.ts +373 -0
  27. package/src/commands/publish.ts +171 -0
  28. package/src/index.ts +8 -0
  29. package/src/inner.ts +11 -0
  30. package/src/merge-package-json.ts +75 -0
  31. package/src/rollup/base.ts +72 -0
  32. package/src/rollup/index.ts +422 -0
  33. package/src/rollup/plugins/globals.ts +34 -0
  34. package/src/rollup/plugins/progress.ts +26 -0
  35. package/src/rollup/plugins/skip.ts +23 -0
  36. package/src/rollup/utils/commonOptions.ts +9 -0
  37. package/src/rollup/utils/externalResolver.ts +21 -0
  38. package/src/rollup/utils/globalResolver.ts +13 -0
  39. package/src/rollup/utils/withMinify.ts +18 -0
  40. package/src/utils/filterSupport.ts +84 -0
  41. package/src/utils/getExports.ts +104 -0
  42. package/src/utils/getRoot.ts +16 -0
  43. package/src/utils/getWD.ts +31 -0
  44. package/src/utils/loadConfig.ts +93 -0
  45. package/src/utils/tsRegister.ts +26 -0
  46. package/index.js +0 -1
@@ -0,0 +1,422 @@
1
+ import '../rollup/base'
2
+
3
+ import fs from 'node:fs'
4
+ import { dirname, relative, resolve } from 'node:path'
5
+
6
+ import type { RecursiveRecord } from '@jiek/pkger/entrypoints'
7
+ import { getAllLeafs } from '@jiek/pkger/entrypoints'
8
+ import { dts } from '@jiek/rollup-plugin-dts'
9
+ import { getWorkspaceDir } from '@jiek/utils/getWorkspaceDir'
10
+ import commonjs from '@rollup/plugin-commonjs'
11
+ import json from '@rollup/plugin-json'
12
+ import { nodeResolve } from '@rollup/plugin-node-resolve'
13
+ import terser from '@rollup/plugin-terser'
14
+ import { sendMessage } from 'execa'
15
+ import { parse } from 'jsonc-parser'
16
+ import { isMatch } from 'micromatch'
17
+ import type { OutputOptions, OutputPlugin, RollupOptions } from 'rollup'
18
+ import esbuild from 'rollup-plugin-esbuild'
19
+ import ts from 'typescript'
20
+
21
+ import { getExports } from '../utils/getExports'
22
+ import { loadConfig } from '../utils/loadConfig'
23
+ import type { RollupProgressEvent, TemplateOptions } from './base'
24
+ import progress from './plugins/progress'
25
+ import skip from './plugins/skip'
26
+ import externalResolver from './utils/externalResolver'
27
+
28
+ interface PackageJSON {
29
+ name?: string
30
+ type?: string
31
+ exports?: Record<string, unknown> | string | string[]
32
+ }
33
+
34
+ const {
35
+ JIEK_ROOT,
36
+ JIEK_ENTRIES
37
+ } = process.env
38
+ const WORKSPACE_ROOT = JIEK_ROOT ?? getWorkspaceDir()
39
+ const COMMON_OPTIONS = {} satisfies RollupOptions
40
+ const COMMON_PLUGINS = [
41
+ json()
42
+ ]
43
+
44
+ const config = loadConfig() ?? {}
45
+ const { build = {} } = config
46
+ const jsOutdir = `./${
47
+ relative(
48
+ process.cwd(),
49
+ resolve(
50
+ (
51
+ typeof build?.output?.dir === 'object'
52
+ // the outdir only affect js output in this function
53
+ ? build.output.dir.js
54
+ : build?.output?.dir
55
+ ) ?? 'dist'
56
+ )
57
+ )
58
+ }`
59
+
60
+ const STYLE_REGEXP = /\.(css|s[ac]ss|less|styl)$/
61
+
62
+ // eslint-disable-next-line unused-imports/no-unused-vars
63
+ const debug = (...args: unknown[]) => sendMessage({ type: 'debug', data: args } satisfies RollupProgressEvent)
64
+
65
+ const resolveWorkspacePath = (p: string) => resolve(WORKSPACE_ROOT, p)
66
+
67
+ const pascalCase = (str: string) =>
68
+ str
69
+ .replace(/[@|/-](\w)/g, (_, $1) => $1.toUpperCase())
70
+ .replace(/(?:^|-)(\w)/g, (_, $1) => $1.toUpperCase())
71
+
72
+ const reveal = (obj: string | Record<string, unknown>, keys: string[]) =>
73
+ keys.reduce((acc, key) => {
74
+ if (typeof acc === 'string') throw new Error('key not found in exports')
75
+ if (!(key in acc)) throw new Error(`key ${key} not found in exports`)
76
+ return acc[key] as string | Record<string, unknown>
77
+ }, obj)
78
+
79
+ const withMinify = (
80
+ output: OutputOptions & {
81
+ plugins?: OutputPlugin[]
82
+ },
83
+ minify = build?.output?.minify
84
+ ) =>
85
+ minify === false
86
+ ? [output]
87
+ : minify === 'only-minify'
88
+ ? [{
89
+ ...output,
90
+ // TODO replace suffix when pubish to npm and the `build.output.minify` is 'only-minify'
91
+ // TODO resolve dts output file name
92
+ file: output.file?.replace(/(\.[cm]?js)$/, '.min$1'),
93
+ plugins: [
94
+ ...(output.plugins ?? []),
95
+ terser()
96
+ ]
97
+ }]
98
+ : [
99
+ output,
100
+ {
101
+ ...output,
102
+ file: output.file?.replace(/(\.[cm]?js)$/, '.min$1'),
103
+ plugins: [
104
+ ...(output.plugins ?? []),
105
+ terser()
106
+ ]
107
+ }
108
+ ]
109
+
110
+ type TSConfig = {
111
+ extends?: string | string[]
112
+ compilerOptions?: Record<string, unknown>
113
+ references?: { path: string }[]
114
+ files?: string[]
115
+ include?: string[]
116
+ exclude?: string[]
117
+ }
118
+
119
+ const getTSConfig = (p: string): TSConfig =>
120
+ !fs.existsSync(p) || !fs.statSync(p).isFile()
121
+ ? {}
122
+ : parse(fs.readFileSync(p, 'utf-8'), [], { allowTrailingComma: true, allowEmptyContent: true })
123
+
124
+ const getExtendTSConfig = (tsconfigPath: string): TSConfig => {
125
+ tsconfigPath = resolve(tsconfigPath)
126
+ const tsconfigPathDirname = dirname(tsconfigPath)
127
+ const { extends: exts, ...tsconfig } = getTSConfig(tsconfigPath)
128
+ const resolvePaths = (paths: string[] | undefined) => paths?.map(p => resolve(tsconfigPathDirname, p)) ?? []
129
+
130
+ const extendsPaths = resolvePaths(
131
+ exts ? Array.isArray(exts) ? exts : [exts] : []
132
+ )
133
+ if (extendsPaths.length === 0) return tsconfig
134
+ return extendsPaths
135
+ .map(getExtendTSConfig)
136
+ .concat(tsconfig)
137
+ // https://www.typescriptlang.org/tsconfig/#files:~:text=Currently%2C%20the%20only%20top%2Dlevel%20property%20that%20is%20excluded%20from%20inheritance%20is%20references.
138
+ // Currently, the only top-level property that is excluded from inheritance is references.
139
+ .reduce((acc, { compilerOptions = {}, references: _, ...curr }) => ({
140
+ ...acc,
141
+ ...curr,
142
+ compilerOptions: {
143
+ ...acc.compilerOptions,
144
+ ...compilerOptions
145
+ }
146
+ }), {})
147
+ }
148
+
149
+ const getCompilerOptionsByFilePath = (tsconfigPath: string, filePath: string): Record<string, unknown> | undefined => {
150
+ tsconfigPath = resolve(tsconfigPath)
151
+ filePath = resolve(filePath)
152
+ const tsconfigPathDirname = dirname(tsconfigPath)
153
+ // https://www.typescriptlang.org/tsconfig/#files:~:text=It%E2%80%99s%20worth%20noting%20that%20files%2C%20include%2C%20and%20exclude%20from%20the%20inheriting%20config%20file%20overwrite%20those%20from%20the%20base%20config%20file%2C%20and%20that%20circularity%20between%20configuration%20files%20is%20not%20allowed.
154
+ // It’s worth noting that files, include, and exclude from the inheriting config file overwrite
155
+ // those from the base config file, and that circularity between configuration files is not allowed.
156
+ const tsconfig = getExtendTSConfig(tsconfigPath)
157
+
158
+ const resolvePaths = (paths: string[] | undefined) => paths?.map(p => resolve(tsconfigPathDirname, p)) ?? []
159
+
160
+ const [
161
+ references,
162
+ files,
163
+ include,
164
+ exclude
165
+ ] = [
166
+ tsconfig.references?.map(({ path }) => path),
167
+ tsconfig.files,
168
+ tsconfig.include,
169
+ tsconfig.exclude
170
+ ].map(resolvePaths)
171
+ if (exclude.length > 0 && exclude.some(i => isMatch(filePath, i))) return
172
+
173
+ // when files or include is not empty, the tsconfig should be ignored
174
+ if (tsconfig.files?.length === 0 && tsconfig.include?.length === 0) return
175
+ let isInclude = false
176
+ isInclude ||= files.length > 0 && files.includes(filePath)
177
+ isInclude ||= include.length > 0 && include.some(i => isMatch(filePath, i))
178
+ if (isInclude) {
179
+ return tsconfig.compilerOptions ?? {}
180
+ } else {
181
+ // when files or include is not empty, but the file is not matched, the tsconfig should be ignored
182
+ if (
183
+ (tsconfig.files && tsconfig.files.length > 0)
184
+ || (tsconfig.include && tsconfig.include.length > 0)
185
+ ) return
186
+ }
187
+
188
+ references.reverse()
189
+ for (const ref of references) {
190
+ const compilerOptions = getCompilerOptionsByFilePath(ref, filePath)
191
+ if (compilerOptions) return compilerOptions
192
+ }
193
+ return tsconfig.compilerOptions
194
+ }
195
+
196
+ const generateConfigs = ({
197
+ path,
198
+ name,
199
+ input,
200
+ output,
201
+ external,
202
+ pkgIsModule,
203
+ conditionals
204
+ }: {
205
+ path: string
206
+ name: string
207
+ input: string
208
+ output: string
209
+ external: (string | RegExp)[]
210
+ pkgIsModule: boolean
211
+ conditionals: string[]
212
+ }, options: TemplateOptions = {}): RollupOptions[] => {
213
+ const isModule = conditionals.includes('import')
214
+ const isCommonJS = conditionals.includes('require')
215
+ const isBrowser = conditionals.includes('browser')
216
+ const dtsTSConfigPaths = [
217
+ resolveWorkspacePath('tsconfig.json'),
218
+ resolveWorkspacePath('tsconfig.dts.json')
219
+ ]
220
+ let dtsTSConfigPath: string | undefined
221
+ dtsTSConfigPaths.forEach(p => {
222
+ if (fs.existsSync(p) && fs.statSync(p).isFile()) {
223
+ dtsTSConfigPath = p
224
+ }
225
+ })
226
+ let compilerOptions: ts.CompilerOptions = {}
227
+ if (dtsTSConfigPath) {
228
+ const jsonCompilerOptions = getCompilerOptionsByFilePath(dtsTSConfigPath, resolve(input))
229
+ const { options, errors } = ts.convertCompilerOptionsFromJson(
230
+ jsonCompilerOptions,
231
+ dirname(dtsTSConfigPath)
232
+ )
233
+ if (errors.length > 0) {
234
+ throw new Error(errors.map(e => e.messageText).join('\n'))
235
+ }
236
+ compilerOptions = options
237
+ }
238
+ const exportConditions = [...conditionals, ...(compilerOptions.customConditions ?? [])]
239
+ const throughEventProps: RollupProgressEvent & { type: 'progress' } = {
240
+ type: 'progress',
241
+ data: { name, path, exportConditions, input }
242
+ }
243
+ const outdir = options?.output?.dir
244
+ return [
245
+ {
246
+ input,
247
+ external,
248
+ output: [
249
+ ...withMinify({
250
+ file: output,
251
+ name,
252
+ interop: 'auto',
253
+ sourcemap: typeof options?.output?.sourcemap === 'object'
254
+ ? options.output.sourcemap.js
255
+ : options?.output?.sourcemap,
256
+ format: isModule ? 'esm' : (
257
+ isCommonJS ? 'cjs' : (
258
+ isBrowser ? 'umd' : (
259
+ pkgIsModule ? 'esm' : 'cjs'
260
+ )
261
+ )
262
+ ),
263
+ strict: typeof options?.output?.strict === 'object'
264
+ ? options.output.strict.js
265
+ : options?.output?.strict
266
+ })
267
+ ],
268
+ plugins: [
269
+ nodeResolve({ exportConditions }),
270
+ import('rollup-plugin-postcss')
271
+ .then(({ default: postcss }) =>
272
+ postcss({
273
+ extract: resolve(output.replace(/\.[cm]?js$/, '.css')),
274
+ minimize: true
275
+ })
276
+ )
277
+ .catch(() => void 0),
278
+ esbuild(),
279
+ commonjs(),
280
+ progress({
281
+ onEvent: (event, message) =>
282
+ sendMessage(
283
+ {
284
+ ...throughEventProps,
285
+ data: { ...throughEventProps.data, event, message, tags: ['js'] }
286
+ } satisfies RollupProgressEvent
287
+ )
288
+ })
289
+ ]
290
+ },
291
+ {
292
+ input,
293
+ external,
294
+ output: [
295
+ {
296
+ dir: resolve((typeof outdir === 'object' ? outdir.dts : outdir) ?? 'dist'),
297
+ sourcemap: typeof options?.output?.sourcemap === 'object'
298
+ ? options.output.sourcemap.dts
299
+ : options?.output?.sourcemap,
300
+ entryFileNames: () =>
301
+ output
302
+ .replace(`${jsOutdir}/`, '')
303
+ .replace(/(\.[cm]?)js$/, '.d$1ts'),
304
+ strict: typeof options?.output?.strict === 'object'
305
+ ? options.output.strict.dts
306
+ : options?.output?.strict
307
+ }
308
+ ],
309
+ plugins: [
310
+ nodeResolve({ exportConditions }),
311
+ skip({ patterns: [STYLE_REGEXP] }),
312
+ dts({
313
+ respectExternal: true,
314
+ compilerOptions
315
+ }),
316
+ progress({
317
+ onEvent: (event, message) =>
318
+ sendMessage(
319
+ {
320
+ ...throughEventProps,
321
+ data: { ...throughEventProps.data, event, message, tags: ['dts'] }
322
+ } satisfies RollupProgressEvent
323
+ )
324
+ })
325
+ ]
326
+ }
327
+ ]
328
+ }
329
+
330
+ export function template(packageJSON: PackageJSON): RollupOptions[] {
331
+ const { name, type, exports: entrypoints } = packageJSON
332
+ const pkgIsModule = type === 'module'
333
+ if (!name) throw new Error('package.json name is required')
334
+ if (!entrypoints) throw new Error('package.json exports is required')
335
+
336
+ const entries = JIEK_ENTRIES
337
+ ?.split(',')
338
+ .map(e => e.trim())
339
+ .map(e => ({
340
+ 'index': '.'
341
+ }[e] ?? e))
342
+
343
+ const packageName = pascalCase(name)
344
+
345
+ const external = externalResolver(packageJSON as Record<string, unknown>)
346
+
347
+ const [filteredResolvedEntrypoints, exports] = getExports({
348
+ entrypoints,
349
+ pkgIsModule,
350
+ entries,
351
+ config
352
+ })
353
+
354
+ const leafMap = new Map<string, string[][]>()
355
+ getAllLeafs(filteredResolvedEntrypoints as RecursiveRecord<string>, ({ keys, value }) => {
356
+ if (typeof value === 'string') {
357
+ const keysArr = leafMap.get(value) ?? []
358
+ leafMap.set(value, keysArr)
359
+ keysArr.push(keys)
360
+ }
361
+ return false
362
+ })
363
+
364
+ const configs: RollupOptions[] = []
365
+ leafMap.forEach((keysArr, input) =>
366
+ keysArr.forEach((keys) => {
367
+ const [path, ...conditionals] = keys
368
+
369
+ const name = packageName + (path === '.' ? '' : pascalCase(path))
370
+ const keyExports = reveal(exports, keys)
371
+ const commonOptions = {
372
+ path,
373
+ name,
374
+ input,
375
+ external,
376
+ pkgIsModule
377
+ }
378
+
379
+ switch (typeof keyExports) {
380
+ case 'string': {
381
+ configs.push(...generateConfigs({
382
+ ...commonOptions,
383
+ output: keyExports,
384
+ conditionals
385
+ }, build))
386
+ break
387
+ }
388
+ case 'object': {
389
+ getAllLeafs(keyExports as RecursiveRecord<string>, ({ keys: nextKeys, value }) => {
390
+ const allConditionals = [...new Set([...conditionals, ...nextKeys])]
391
+ if (typeof value === 'string') {
392
+ configs.push(...generateConfigs({
393
+ ...commonOptions,
394
+ output: value,
395
+ conditionals: allConditionals
396
+ }, build))
397
+ }
398
+ return false
399
+ })
400
+ break
401
+ }
402
+ }
403
+ })
404
+ )
405
+ sendMessage(
406
+ {
407
+ type: 'init',
408
+ data: {
409
+ leafMap,
410
+ targetsLength: configs.length
411
+ }
412
+ } satisfies RollupProgressEvent
413
+ )
414
+ return configs.map(c => ({
415
+ ...COMMON_OPTIONS,
416
+ ...c,
417
+ plugins: [
418
+ ...COMMON_PLUGINS,
419
+ c.plugins
420
+ ]
421
+ }))
422
+ }
@@ -0,0 +1,34 @@
1
+ import type { Plugin, PluginImpl } from 'rollup'
2
+
3
+ import globalResolver from '../utils/globalResolver'
4
+
5
+ interface GlobalsOptions {
6
+ external?: (string | RegExp)[]
7
+ }
8
+
9
+ export function createGlobalsLinkage() {
10
+ let globals = {}
11
+ const dependencies = new Set<string>([])
12
+ return [
13
+ (({ external } = {}) => {
14
+ return {
15
+ name: 'globals',
16
+ resolveId(id) {
17
+ if (external?.some(dep => dep instanceof RegExp ? dep.test(id) : dep === id)) {
18
+ dependencies.add(id)
19
+ return { id, external: true }
20
+ }
21
+ return null
22
+ },
23
+ outputOptions(options) {
24
+ globals = [...dependencies].reduce((acc, value) => ({
25
+ ...acc,
26
+ [value]: globalResolver(value)
27
+ }), {})
28
+ return { ...options, globals }
29
+ }
30
+ }
31
+ }) as PluginImpl<GlobalsOptions>,
32
+ { outputOptions: options => ({ ...options, globals }) } as Plugin
33
+ ] as const
34
+ }
@@ -0,0 +1,26 @@
1
+ import type { PluginImpl } from 'rollup'
2
+
3
+ interface Options {
4
+ onEvent?: (event: string, message?: string) => void
5
+ }
6
+
7
+ export default ((options = {}) => {
8
+ const { onEvent } = options
9
+ return {
10
+ name: 'progress',
11
+ buildStart: () => onEvent?.('start', 'Start building...'),
12
+ buildEnd: () => onEvent?.('end', 'Build completed!'),
13
+ resolveId: {
14
+ order: 'post',
15
+ handler: source => onEvent?.('resolve', `Resolving ${source}...`)
16
+ },
17
+ load: {
18
+ order: 'post',
19
+ handler: id => onEvent?.('load', `Loading ${id}...`)
20
+ },
21
+ transform: {
22
+ order: 'post',
23
+ handler: (_, id) => onEvent?.('transform', `Transforming ${id}...`)
24
+ }
25
+ }
26
+ }) as PluginImpl<Options>
@@ -0,0 +1,23 @@
1
+ import type { PluginImpl } from 'rollup'
2
+
3
+ interface Options {
4
+ patterns?: (string | RegExp)[]
5
+ }
6
+
7
+ export default ((options = {}) => {
8
+ return {
9
+ name: 'skip',
10
+ // skip the specified files by `options.patterns`
11
+ load(id) {
12
+ if (
13
+ options.patterns?.some((pattern) =>
14
+ typeof pattern === 'string'
15
+ ? id.includes(pattern)
16
+ : pattern.test(id)
17
+ )
18
+ ) {
19
+ return ''
20
+ }
21
+ }
22
+ }
23
+ }) as PluginImpl<Options>
@@ -0,0 +1,9 @@
1
+ import type { OutputOptions } from 'rollup'
2
+
3
+ const defineOutput = <O extends OutputOptions>(output: O) => output
4
+
5
+ export const commonOutputOptions = defineOutput({
6
+ exports: 'named',
7
+ interop: 'auto',
8
+ sourcemap: true
9
+ })
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs'
2
+
3
+ export default function(json: Record<string, unknown>): (string | RegExp)[]
4
+ export default function(path?: string): (string | RegExp)[]
5
+ export default function(jsonOrPath: string | Record<string, unknown> = process.cwd()): (string | RegExp)[] {
6
+ const pkg = typeof jsonOrPath === 'string'
7
+ ? fs.existsSync(`${jsonOrPath}/package.json`)
8
+ ? JSON.parse(fs.readFileSync(`${jsonOrPath}/package.json`, 'utf-8'))
9
+ : {}
10
+ : jsonOrPath
11
+ const { dependencies = {}, peerDependencies = {}, optionalDependencies = {} } = pkg
12
+ const external = <(string | RegExp)[]> Object
13
+ .keys(dependencies)
14
+ .concat(Object.keys(peerDependencies))
15
+ .concat(Object.keys(optionalDependencies))
16
+ return external
17
+ .map(dep => new RegExp(`^${dep}(/.*)?$`))
18
+ .concat([
19
+ /^node:/
20
+ ])
21
+ }
@@ -0,0 +1,13 @@
1
+ export default function (external: string) {
2
+ // a/b => AB
3
+ // a-b => AB
4
+ // a@b => AB
5
+ // a@b/c => ABC
6
+ // node:a => a
7
+ // node:a_b => a_b
8
+ if (external.startsWith('node:')) {
9
+ return external.slice(5)
10
+ }
11
+ return external
12
+ .replace(/[@|/-](\w)/g, (_, $1) => $1.toUpperCase())
13
+ }
@@ -0,0 +1,18 @@
1
+ import terser from '@rollup/plugin-terser'
2
+ import type { OutputOptions, OutputPlugin } from 'rollup'
3
+
4
+ export default function(
5
+ output: OutputOptions & {
6
+ entryFileNames?: string
7
+ plugins?: OutputPlugin[]
8
+ }
9
+ ): OutputOptions[] {
10
+ return [
11
+ output,
12
+ {
13
+ ...output,
14
+ entryFileNames: output.entryFileNames?.replace(/(\.[cm]?js)$/, '.min$1'),
15
+ plugins: [...(output.plugins ?? []), terser()]
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,84 @@
1
+ import fs from 'node:fs'
2
+ import { createRequire } from 'node:module'
3
+ import path from 'node:path'
4
+
5
+ import { filterPackagesFromDir } from '@pnpm/filter-workspace-packages'
6
+ import { program } from 'commander'
7
+ import { load } from 'js-yaml'
8
+
9
+ import { getRoot } from './getRoot'
10
+ import { getWD } from './getWD'
11
+
12
+ export let type = ''
13
+
14
+ try {
15
+ const require = createRequire(import.meta.url)
16
+ require.resolve('@pnpm/filter-workspace-packages')
17
+ type = 'pnpm'
18
+ } catch { /* empty */ }
19
+ if (type !== '') {
20
+ program
21
+ .option('-f, --filter <filter>', 'filter packages')
22
+ }
23
+
24
+ interface ProjectsGraph {
25
+ wd: string
26
+ root: string
27
+ value?: Record<string, {
28
+ name?: string
29
+ type?: string
30
+ exports?: string | string[] | Record<string, unknown>
31
+ }>
32
+ }
33
+
34
+ export async function getSelectedProjectsGraph(): Promise<ProjectsGraph> {
35
+ let filter = program.getOptionValue('filter')
36
+ const root = getRoot()
37
+ const { wd, notWorkspace } = getWD()
38
+ if (!notWorkspace && type === 'pnpm') {
39
+ const pnpmWorkspaceFilePath = path.resolve(wd, 'pnpm-workspace.yaml')
40
+ const pnpmWorkspaceFileContent = fs.readFileSync(pnpmWorkspaceFilePath, 'utf-8')
41
+ const pnpmWorkspace = load(pnpmWorkspaceFileContent) as {
42
+ packages: string[]
43
+ }
44
+ if (root === wd && !filter) {
45
+ throw new Error('root path is workspace root, please provide a filter')
46
+ // TODO inquirer prompt support user select packages
47
+ }
48
+ if (root !== wd && !filter) {
49
+ const packageJSONIsExist = fs.existsSync(path.resolve(root, 'package.json'))
50
+ if (!packageJSONIsExist) {
51
+ throw new Error('root path is not workspace root, please provide a filter')
52
+ }
53
+ const packageJSON = JSON.parse(fs.readFileSync(path.resolve(root, 'package.json'), 'utf-8'))
54
+ if (!packageJSON.name) {
55
+ throw new Error('root path is not workspace root, please provide a filter')
56
+ }
57
+ filter = packageJSON.name
58
+ }
59
+ const { selectedProjectsGraph } = await filterPackagesFromDir(wd, [{
60
+ filter: filter ?? '',
61
+ followProdDepsOnly: true
62
+ }], {
63
+ prefix: root,
64
+ workspaceDir: wd,
65
+ patterns: pnpmWorkspace.packages
66
+ })
67
+ return {
68
+ wd,
69
+ root,
70
+ value: Object.entries(selectedProjectsGraph)
71
+ .reduce((acc, [key, value]) => {
72
+ acc[key] = value.package.manifest
73
+ return acc
74
+ }, {} as NonNullable<ProjectsGraph['value']>)
75
+ }
76
+ }
77
+ return {
78
+ wd,
79
+ root,
80
+ value: {
81
+ [wd]: JSON.parse(fs.readFileSync(path.resolve(wd, 'package.json'), 'utf-8'))
82
+ }
83
+ }
84
+ }