jiek 2.0.2-alpha.12 → 2.0.2-alpha.14

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.
@@ -0,0 +1,370 @@
1
+ import * as childProcess from 'node:child_process'
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+
5
+ import { bump, type BumperType, TAGS } from '@jiek/utils/bumper'
6
+ import { program } from 'commander'
7
+ import detectIndent from 'detect-indent'
8
+ import { applyEdits, modify } from 'jsonc-parser'
9
+
10
+ import type { ProjectsGraph } from '../utils/filterSupport'
11
+ import { getSelectedProjectsGraph } from '../utils/filterSupport'
12
+ import { getExports } from '../utils/getExports'
13
+ import { loadConfig } from '../utils/loadConfig'
14
+ import { outdirDescription } from './descriptions'
15
+
16
+ declare module 'jiek' {
17
+ export interface Config {
18
+ publish?: {
19
+ /**
20
+ * @default false
21
+ */
22
+ withSuffix?: boolean
23
+ /**
24
+ * @default true
25
+ */
26
+ withSource?: boolean
27
+ }
28
+ }
29
+ }
30
+
31
+ const description = `
32
+ Publish package to npm registry, and auto generate exports field and other fields in published package.json.
33
+ If you want to through the options to the \`pnpm publish\` command, you can pass the options after '--'.
34
+ `.trim()
35
+
36
+ async function forEachSelectedProjectsGraphEntries(
37
+ callback: (dir: string, manifest: NonNullable<ProjectsGraph['value']>[string]) => void
38
+ ) {
39
+ const { value = {} } = await getSelectedProjectsGraph() ?? {}
40
+ const selectedProjectsGraphEntries = Object.entries(value)
41
+ if (selectedProjectsGraphEntries.length === 0) {
42
+ throw new Error('no packages selected')
43
+ }
44
+ for (const [dir, manifest] of selectedProjectsGraphEntries) {
45
+ callback(dir, manifest)
46
+ }
47
+ }
48
+
49
+ program
50
+ .command('publish')
51
+ .description(description)
52
+ .aliases(['pub', 'p'])
53
+ .option('-b, --bumper <bumper>', 'bump version', 'patch')
54
+ .option('-no-b, --no-bumper', 'no bump version')
55
+ .option('-o, --outdir <OUTDIR>', outdirDescription, String, 'dist')
56
+ .action(async ({ outdir, bumper }: {
57
+ outdir?: string
58
+ bumper: false | BumperType
59
+ }) => {
60
+ let shouldPassThrough = false
61
+
62
+ const passThroughOptions = process.argv
63
+ .reduce(
64
+ (acc, value) => {
65
+ if (shouldPassThrough) {
66
+ acc.push(value)
67
+ }
68
+ if (value === '--') {
69
+ shouldPassThrough = true
70
+ }
71
+ return acc
72
+ },
73
+ [] as string[]
74
+ )
75
+
76
+ await forEachSelectedProjectsGraphEntries(dir => {
77
+ const args = ['pnpm', 'publish', '--access', 'public', '--no-git-checks']
78
+ if (bumper && TAGS.includes(bumper)) {
79
+ args.push('--tag', bumper)
80
+ }
81
+ args.push(...passThroughOptions)
82
+ childProcess.execSync(args.join(' '), {
83
+ cwd: dir,
84
+ stdio: 'inherit',
85
+ env: {
86
+ ...process.env,
87
+ JIEK_PUBLISH_OUTDIR: JSON.stringify(outdir),
88
+ JIEK_PUBLISH_BUMPER: JSON.stringify(bumper)
89
+ }
90
+ })
91
+ })
92
+ })
93
+
94
+ async function prepublish() {
95
+ const {
96
+ JIEK_PUBLISH_OUTDIR: outdirEnv,
97
+ JIEK_PUBLISH_BUMPER: bumperEnv
98
+ } = process.env
99
+ const outdir = outdirEnv ? JSON.parse(outdirEnv) : 'dist'
100
+ const bumper = bumperEnv ? JSON.parse(bumperEnv) : false
101
+
102
+ const generateNewManifest = (dir: string, manifest: NonNullable<ProjectsGraph['value']>[string]) => {
103
+ const { name, type, exports: entrypoints = {} } = manifest
104
+ if (!name) {
105
+ throw new Error(`package.json in ${dir} must have a name field`)
106
+ }
107
+
108
+ const pkgIsModule = type === 'module'
109
+ const newManifest = { ...manifest }
110
+ const [resolvedEntrypoints, exports, resolvedOutdir] = getExports({
111
+ entrypoints,
112
+ pkgIsModule,
113
+ pkgName: name,
114
+ config: loadConfig(dir),
115
+ dir,
116
+ defaultOutdir: outdir,
117
+ noFilter: true,
118
+ isPublish: true
119
+ })
120
+ newManifest.exports = {
121
+ ...resolvedEntrypoints,
122
+ ...exports
123
+ }
124
+ return [newManifest, resolvedOutdir] as const
125
+ }
126
+
127
+ const generateNewPackageJSONString = ({
128
+ oldJSONString,
129
+ oldJSON,
130
+ manifest,
131
+ formattingOptions
132
+ }: {
133
+ oldJSONString: string
134
+ oldJSON: Record<string, unknown>
135
+ manifest: NonNullable<ProjectsGraph['value']>[string]
136
+ formattingOptions: {
137
+ tabSize: number
138
+ insertSpaces: boolean
139
+ }
140
+ }) => {
141
+ let newJSONString = oldJSONString
142
+ newJSONString = applyEdits(
143
+ newJSONString,
144
+ modify(
145
+ newJSONString,
146
+ ['publishConfig', 'typesVersions'],
147
+ {
148
+ '<5.0': {
149
+ '*': [
150
+ '*',
151
+ `./*`,
152
+ `./*/index.d.ts`,
153
+ `./*/index.d.mts`,
154
+ `./*/index.d.cts`
155
+ ]
156
+ }
157
+ },
158
+ { formattingOptions }
159
+ )
160
+ )
161
+ for (const [key, value] of Object.entries(manifest)) {
162
+ if (key === 'version') continue
163
+ if (JSON.stringify(value) === JSON.stringify(oldJSON[key])) continue
164
+
165
+ if (key !== 'exports') {
166
+ newJSONString = applyEdits(
167
+ newJSONString,
168
+ modify(
169
+ newJSONString,
170
+ ['publishConfig', key],
171
+ value,
172
+ { formattingOptions }
173
+ )
174
+ )
175
+ } else {
176
+ const exports = value as Record<string, unknown>
177
+ for (const [k, v] of Object.entries(exports)) {
178
+ newJSONString = applyEdits(
179
+ newJSONString,
180
+ modify(
181
+ newJSONString,
182
+ ['publishConfig', 'exports', k],
183
+ v,
184
+ { formattingOptions }
185
+ )
186
+ )
187
+ }
188
+ const index = exports?.['.']
189
+ const indexPublishConfig: Record<string, string> = {}
190
+ if (index) {
191
+ switch (typeof index) {
192
+ case 'string':
193
+ indexPublishConfig[
194
+ manifest?.type === 'module' ? 'module' : 'main'
195
+ ] = index
196
+ break
197
+ case 'object': {
198
+ const indexExports = index as Record<string, string>
199
+ indexPublishConfig.main = indexExports['require'] ?? indexExports['default']
200
+ indexPublishConfig.module = indexExports['import'] ?? indexExports['module'] ?? indexExports['default']
201
+ break
202
+ }
203
+ }
204
+ for (const [k, v] of Object.entries(indexPublishConfig)) {
205
+ if (v === undefined) continue
206
+ newJSONString = applyEdits(
207
+ newJSONString,
208
+ modify(
209
+ newJSONString,
210
+ ['publishConfig', k],
211
+ v,
212
+ { formattingOptions }
213
+ )
214
+ )
215
+ }
216
+ }
217
+ }
218
+ }
219
+ if (oldJSON['peerDependencies']) {
220
+ const peerDependenciesMeta = Object.keys(oldJSON['peerDependencies']).reduce(
221
+ (acc, key) => {
222
+ acc[key] = { optional: true }
223
+ return acc
224
+ },
225
+ {} as Record<string, { optional: boolean }>
226
+ )
227
+ newJSONString = applyEdits(
228
+ newJSONString,
229
+ modify(
230
+ newJSONString,
231
+ ['peerDependenciesMeta'],
232
+ peerDependenciesMeta,
233
+ { formattingOptions }
234
+ )
235
+ )
236
+ }
237
+ if (oldJSON['files']) {
238
+ newJSONString = applyEdits(
239
+ newJSONString,
240
+ modify(
241
+ newJSONString,
242
+ ['files'],
243
+ undefined,
244
+ { formattingOptions }
245
+ )
246
+ )
247
+ }
248
+ return newJSONString
249
+ }
250
+
251
+ await forEachSelectedProjectsGraphEntries((dir, originalManifest) => {
252
+ const [manifest, resolvedOutdir] = generateNewManifest(dir, originalManifest)
253
+ const resolveByDir = (...paths: string[]) => path.resolve(dir, ...paths)
254
+
255
+ const oldJSONString = fs.readFileSync(resolveByDir('package.json'), 'utf-8')
256
+ const oldJSON = JSON.parse(oldJSONString) ?? '0.0.0'
257
+ if (typeof oldJSON.version !== 'string') {
258
+ throw new Error(`${dir}/package.json must have a version field with a string value`)
259
+ }
260
+
261
+ // TODO detectIndent by editorconfig
262
+ const { indent = ' ' } = detectIndent(oldJSONString)
263
+ const formattingOptions = {
264
+ tabSize: indent.length,
265
+ insertSpaces: true
266
+ }
267
+
268
+ const newVersion = bumper ? bump(oldJSON.version, bumper) : oldJSON.version
269
+ const modifyVersionPackageJSON = applyEdits(
270
+ oldJSONString,
271
+ modify(oldJSONString, ['version'], newVersion, { formattingOptions })
272
+ )
273
+
274
+ const newJSONString = generateNewPackageJSONString({
275
+ oldJSONString: modifyVersionPackageJSON,
276
+ oldJSON: {
277
+ ...oldJSON,
278
+ version: newVersion
279
+ },
280
+ manifest,
281
+ formattingOptions
282
+ })
283
+
284
+ const withPublishConfigDirectoryOldJSONString = applyEdits(
285
+ modifyVersionPackageJSON,
286
+ modify(modifyVersionPackageJSON, ['publishConfig', 'directory'], resolvedOutdir, { formattingOptions })
287
+ )
288
+
289
+ if (!fs.existsSync(resolveByDir(resolvedOutdir))) {
290
+ fs.mkdirSync(resolveByDir(resolvedOutdir))
291
+ }
292
+ const jiekTempDir = resolveByDir('node_modules/.jiek/.tmp')
293
+ if (!fs.existsSync(resolveByDir(jiekTempDir))) {
294
+ fs.mkdirSync(resolveByDir(jiekTempDir), { recursive: true })
295
+ }
296
+
297
+ fs.writeFileSync(resolveByDir(resolvedOutdir, 'package.json'), newJSONString)
298
+ fs.writeFileSync(resolveByDir(jiekTempDir, 'package.json'), modifyVersionPackageJSON)
299
+ fs.writeFileSync(resolveByDir('package.json'), withPublishConfigDirectoryOldJSONString)
300
+
301
+ if (oldJSON.files) {
302
+ if (!Array.isArray(oldJSON.files)) {
303
+ throw new Error(`${dir}/package.json files field must be an array`)
304
+ }
305
+ if (Array.isArray(oldJSON.files) && oldJSON.files.every((file: unknown) => typeof file !== 'string')) {
306
+ throw new Error(`${dir}/package.json files field must be an array of string`)
307
+ }
308
+ }
309
+ const resolvedOutdirAbs = resolveByDir(resolvedOutdir)
310
+ const files = (
311
+ (oldJSON.files as undefined | string[]) ?? fs.readdirSync(resolveByDir('.'))
312
+ ).filter(file => file === 'node_modules' || resolveByDir(file) !== resolvedOutdirAbs)
313
+
314
+ for (const file of files) {
315
+ const path = resolveByDir(file)
316
+ try {
317
+ const stat = fs.statSync(path)
318
+ if (stat.isDirectory()) {
319
+ fs.cpSync(path, resolveByDir(resolvedOutdir, file), { recursive: true })
320
+ continue
321
+ }
322
+ if (stat.isFile()) {
323
+ fs.cpSync(path, resolveByDir(resolvedOutdir, file))
324
+ continue
325
+ }
326
+ } catch (e) {
327
+ console.warn(String(e))
328
+ continue
329
+ }
330
+ throw new Error(`file type of ${path} is not supported`)
331
+ }
332
+ })
333
+ }
334
+
335
+ async function postpublish() {
336
+ await forEachSelectedProjectsGraphEntries(dir => {
337
+ const jiekTempDir = path.resolve(dir, 'node_modules/.jiek/.tmp')
338
+ const packageJSON = path.resolve(dir, 'package.json')
339
+ const jiekTempPackageJSON = path.resolve(jiekTempDir, 'package.json')
340
+ if (fs.existsSync(jiekTempPackageJSON)) {
341
+ fs.copyFileSync(jiekTempPackageJSON, packageJSON)
342
+ fs.rmSync(jiekTempPackageJSON)
343
+ console.log(`${dir}/package.json has been restored`)
344
+ } else {
345
+ throw new Error(
346
+ `jiek temp \`${dir}/package.json\` not found, please confirm the jiek pre-publish command has been executed`
347
+ )
348
+ }
349
+ })
350
+ }
351
+
352
+ program
353
+ .action(async () => {
354
+ const {
355
+ npm_lifecycle_event: NPM_LIFECYCLE_EVENT
356
+ } = process.env
357
+ switch (NPM_LIFECYCLE_EVENT) {
358
+ case 'prepublish':
359
+ await prepublish()
360
+ break
361
+ case 'postpublish':
362
+ await postpublish()
363
+ break
364
+ default:
365
+ program.help()
366
+ }
367
+ })
368
+
369
+ program.command('prepublish').action(prepublish)
370
+ program.command('postpublish').action(postpublish)
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type {} from './commands/base'
2
+ import type {} from './commands/build'
3
+ import type {} from './commands/init'
4
+ import type {} from './commands/publish'
5
+
6
+ export interface Config {}
7
+
8
+ export const defineConfig = (config: Config) => config
package/src/inner.ts ADDED
@@ -0,0 +1,11 @@
1
+ let resolve: () => void
2
+
3
+ export let actionFuture: Promise<void>
4
+
5
+ export function actionDone() {
6
+ resolve()
7
+ }
8
+
9
+ export function actionRestore() {
10
+ actionFuture = new Promise<void>(r => resolve = r)
11
+ }
@@ -0,0 +1,137 @@
1
+ import type { InputPluginOption, OutputOptions } from 'rollup'
2
+
3
+ export type Mapping2ROO<K extends keyof OutputOptions> = OutputOptions[K] | {
4
+ js?: OutputOptions[K]
5
+ dts?: OutputOptions[K]
6
+ }
7
+
8
+ export interface ConfigGenerateContext {
9
+ path: string
10
+ name: string
11
+ input: string
12
+ output: string
13
+ external: (string | RegExp)[]
14
+ pkgIsModule: boolean
15
+ conditionals: string[]
16
+ }
17
+
18
+ export type OutputControl = boolean | ((context: ConfigGenerateContext) => boolean)
19
+
20
+ export const BUILDER_TYPES = ['esbuild', 'swc'] as const
21
+
22
+ export const BUILDER_TYPE_PACKAGE_NAME_MAP = {
23
+ esbuild: 'rollup-plugin-esbuild',
24
+ swc: 'rollup-plugin-swc3'
25
+ }
26
+
27
+ export interface TemplateOptions {
28
+ /**
29
+ * When the user configures type: module, the generated output from entry points that don't
30
+ * have cts as a suffix will automatically include the CJS version.
31
+ * if it is not configured, and the generated output from entry points that do not have mts
32
+ * as a suffix will automatically include the ESM version.
33
+ *
34
+ * @default true
35
+ */
36
+ crossModuleConvertor?: boolean
37
+ /**
38
+ * Auto-detect the builder from the installed dependencies.
39
+ * If the builder is not installed, it will prompt the user to install it.
40
+ * If exists multiple builders, it will fall back to the 'esbuild'.
41
+ *
42
+ * @default 'esbuild'
43
+ */
44
+ builder?:
45
+ | typeof BUILDER_TYPES[number]
46
+ | ({
47
+ type: 'esbuild'
48
+ } & import('rollup-plugin-esbuild').Options)
49
+ | ({
50
+ type: 'swc'
51
+ } & import('rollup-plugin-swc3').PluginOptions)
52
+ output?: {
53
+ /**
54
+ * @default true
55
+ *
56
+ * When minify is set to true, the output will with minified files.
57
+ * When minify is set to 'only-minify', the output will direct output minified files.
58
+ */
59
+ minify?: boolean | 'only-minify'
60
+ minifyOptions?:
61
+ | typeof BUILDER_TYPES[number]
62
+ | 'terser'
63
+ | (
64
+ {
65
+ type: 'terser'
66
+ } & import('@rollup/plugin-terser').Options
67
+ )
68
+ | (
69
+ {
70
+ type: 'esbuild'
71
+ } & Parameters<typeof import('rollup-plugin-esbuild').minify>[0]
72
+ )
73
+ | (
74
+ {
75
+ type: 'swc'
76
+ } & Parameters<typeof import('rollup-plugin-swc3').minify>[0]
77
+ )
78
+ /**
79
+ * @default 'dist'
80
+ */
81
+ dir?: Mapping2ROO<'dir'>
82
+ sourcemap?: Mapping2ROO<'sourcemap'>
83
+ strict?: Mapping2ROO<'strict'>
84
+ js?: OutputControl
85
+ dts?: OutputControl
86
+ }
87
+ /**
88
+ * Set the external dependencies of the package.
89
+ */
90
+ external?: (string | RegExp)[]
91
+ plugins?:
92
+ | InputPluginOption
93
+ | ((type: 'js' | 'dts', context: ConfigGenerateContext) => InputPluginOption)
94
+ | {
95
+ js: InputPluginOption
96
+ dts?: InputPluginOption
97
+ }
98
+ | {
99
+ js?: InputPluginOption
100
+ dts: InputPluginOption
101
+ }
102
+ }
103
+
104
+ export type RollupProgressEvent =
105
+ | {
106
+ type: 'init'
107
+ data: {
108
+ leafMap: Map<string, string[][]>
109
+ targetsLength: number
110
+ }
111
+ }
112
+ | {
113
+ type: 'watchChange'
114
+ data: {
115
+ id: string
116
+ name: string
117
+ path: string
118
+ input: string
119
+ }
120
+ }
121
+ | {
122
+ type: 'debug'
123
+ data: unknown
124
+ }
125
+ | {
126
+ type: 'progress'
127
+ data: {
128
+ // name, path, exportConditions, input
129
+ name: string
130
+ path: string
131
+ exportConditions: string[]
132
+ input: string
133
+ tags?: string[]
134
+ event?: string
135
+ message?: string
136
+ }
137
+ }