jiek 2.3.1 → 2.3.3

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jiek",
3
3
  "type": "module",
4
- "version": "2.3.1",
4
+ "version": "2.3.3",
5
5
  "description": "A lightweight toolkit for compiling and managing libraries based on `package.json` metadata and suitable for `Monorepo`.",
6
6
  "author": "YiJie <yijie4188@gmail.com>",
7
7
  "homepage": "https://github.com/NWYLZW/jiek/tree/master/packages/jiek#readme",
@@ -35,6 +35,11 @@
35
35
  "jiek/__source__": "./src/rollup/index.ts",
36
36
  "require": "./dist/rollup/index.cjs",
37
37
  "default": "./dist/rollup/index.js"
38
+ },
39
+ "./rollup-plugin-utils": {
40
+ "jiek/__source__": "./src/rollup-plugin-utils.ts",
41
+ "require": "./dist/rollup-plugin-utils.cjs",
42
+ "default": "./dist/rollup-plugin-utils.js"
38
43
  }
39
44
  },
40
45
  "imports": {
@@ -51,6 +56,7 @@
51
56
  "jb": "bin/build.cjs"
52
57
  },
53
58
  "peerDependencies": {
59
+ "@ast-grep/napi": "^0.32.3",
54
60
  "@pnpm/filter-workspace-packages": "^7.2.13||^8.0.0||^9.0.0||^10.0.0||>=1000.0.0",
55
61
  "@rollup/plugin-terser": "^0.4.4",
56
62
  "esbuild-register": "^3.5.0",
@@ -74,14 +80,15 @@
74
80
  "js-yaml": "^4.1.0",
75
81
  "jsonc-parser": "^3.2.1",
76
82
  "koa": "^2.15.3",
83
+ "magic-string": "^0.30.17",
77
84
  "rollup": "^4.0.0",
78
85
  "@jiek/pkger": "^0.2.2",
79
86
  "@jiek/utils": "^0.2.3"
80
87
  },
81
- "publishConfig": {
82
- "directory": "./dist/.internal"
83
- },
84
88
  "peerDependenciesMeta": {
89
+ "@ast-grep/napi": {
90
+ "optional": true
91
+ },
85
92
  "@pnpm/filter-workspace-packages": {
86
93
  "optional": true
87
94
  },
@@ -0,0 +1 @@
1
+ {"type":"module","module":"../dist/rollup-plugin-utils.js","main":"../dist/rollup-plugin-utils.cjs"}
@@ -1,4 +1,5 @@
1
1
  import type { InputPluginOption, OutputOptions } from 'rollup'
2
+ import { ReplaceOptions, Replacements } from './plugins/replace'
2
3
 
3
4
  export type Mapping2ROO<K extends keyof OutputOptions> = OutputOptions[K] | {
4
5
  js?: OutputOptions[K]
@@ -134,4 +135,18 @@ export interface TemplateOptions {
134
135
  * ```
135
136
  */
136
137
  injects?: Record<string, string | [string, string]>
138
+ /**
139
+ * Replace the specified content in the code.
140
+ *
141
+ * @example
142
+ * ```js
143
+ * {
144
+ * 'process.env.DEBUG': 'false',
145
+ * 'process.env.NODE_ENV': JSON.stringify('production'),
146
+ * 'process.env.BUILD_PATH': ctx => JSON.stringify(ctx.id)
147
+ * }
148
+ * ```
149
+ */
150
+ replacements?: Replacements
151
+ replacementsOptions?: Pick<ReplaceOptions, 'mode' | 'include' | 'exclude'>
137
152
  }
@@ -27,6 +27,7 @@ import { getCompilerOptionsByFilePath } from '#~/utils/ts'
27
27
  import type { ConfigGenerateContext, TemplateOptions } from './base'
28
28
  import createRequire, { CREATE_REQUIRE_VIRTUAL_MODULE_NAME } from './plugins/create-require'
29
29
  import progress from './plugins/progress'
30
+ import replace from './plugins/replace'
30
31
  import skip from './plugins/skip'
31
32
  import withExternal from './plugins/with-external.ts'
32
33
  import type { PackageJSON } from './utils/externalResolver'
@@ -423,7 +424,14 @@ const generateConfigs = (
423
424
  const { js: jsOutput, dts: dtsOutput } = resolveOutputControls(context, build.output)
424
425
  const rollupOptions: RollupOptions[] = []
425
426
 
426
- const commonPlugins: InputPluginOption[] = [
427
+ const commonPlugins = (
428
+ { sourcemap }: { sourcemap?: string | boolean }
429
+ ): InputPluginOption[] => [
430
+ replace({
431
+ ...build.replacementsOptions,
432
+ sourcemap: sourcemap === 'hidden' ? false : !!sourcemap,
433
+ values: build.replacements
434
+ }),
427
435
  ...inputCommonPlugins,
428
436
  withExternal(),
429
437
  !disableCollectInternalModule && {
@@ -570,7 +578,7 @@ const generateConfigs = (
570
578
  )
571
579
  ],
572
580
  plugins: [
573
- ...commonPlugins,
581
+ ...commonPlugins({ sourcemap }),
574
582
  nodeResolvePluginInstance,
575
583
  import('rollup-plugin-postcss')
576
584
  .then(({ default: postcss }) =>
@@ -630,7 +638,7 @@ const generateConfigs = (
630
638
  }
631
639
  ],
632
640
  plugins: [
633
- ...commonPlugins,
641
+ ...commonPlugins({ sourcemap }),
634
642
  skip({ patterns: [STYLE_REGEXP] }),
635
643
  dts({
636
644
  respectExternal: true,
@@ -0,0 +1,185 @@
1
+ import { extname } from 'node:path'
2
+
3
+ import type { Lang, SgNode } from '@ast-grep/napi'
4
+ import type { FilterOptions } from 'jiek/rollup-plugin-utils'
5
+ import { createFilter, definePlugin } from 'jiek/rollup-plugin-utils'
6
+ import MagicString from 'magic-string'
7
+
8
+ export type Mode = 'string' | 'ast-grep'
9
+
10
+ export type ReplacementFuncCtx =
11
+ & {
12
+ type: 'transform' | 'renderChunk'
13
+ id: string
14
+ code: string
15
+ mode: Mode
16
+ }
17
+ & (
18
+ | {
19
+ mode?: 'string'
20
+ start: number
21
+ end: number
22
+ }
23
+ | {
24
+ mode?: 'ast-grep'
25
+ $:
26
+ & ((name: string) => string | undefined)
27
+ & ((template: { raw: readonly string[] }) => string | undefined)
28
+ node: SgNode
29
+ lang: Lang
30
+ }
31
+ )
32
+
33
+ type Falsy = false | null | undefined
34
+
35
+ export type ReplacementFunc = (ctx: ReplacementFuncCtx) => string | Falsy
36
+
37
+ export type Replacements = Record<
38
+ string,
39
+ string | Falsy | ReplacementFunc
40
+ >
41
+
42
+ export type ReplaceOptions =
43
+ & FilterOptions
44
+ & {
45
+ /**
46
+ * @default 'string'
47
+ */
48
+ mode?: Mode
49
+ sourcemap?: boolean
50
+ values?: Replacements
51
+ }
52
+
53
+ export default definePlugin((options: ReplaceOptions = {}) => {
54
+ const {
55
+ include = [/\.[cm]?[tj]sx?$/],
56
+ exclude = [/node_modules/],
57
+ values = {},
58
+ sourcemap
59
+ } = options
60
+ let { mode = 'string' } = options
61
+ const allValues = { ...values }
62
+ const allKeys = Object.keys(allValues)
63
+ const filter = createFilter({ include, exclude })
64
+
65
+ const replaceAll = async (ctx: Pick<ReplacementFuncCtx, 'type' | 'id'>, code: string) => {
66
+ const ms = new MagicString(code)
67
+ if (mode === 'string') {
68
+ allKeys.forEach(key => {
69
+ const reg = new RegExp(key, 'g')
70
+ let match
71
+ // eslint-disable-next-line no-cond-assign
72
+ while ((match = reg.exec(code))) {
73
+ const start = match.index
74
+ const end = start + key.length
75
+ const value = typeof allValues[key] === 'function'
76
+ ? allValues[key]({
77
+ ...ctx,
78
+ code,
79
+ start,
80
+ end,
81
+ mode: 'string'
82
+ })
83
+ : allValues[key]
84
+ if (([null, undefined, false] as unknown[]).includes(value)) continue
85
+ ms.overwrite(
86
+ match.index,
87
+ match.index + key.length,
88
+ value as string
89
+ )
90
+ }
91
+ })
92
+ } else if (mode === 'ast-grep') {
93
+ const ext = extname(ctx.id)
94
+ const { parse, Lang } = await import('@ast-grep/napi')
95
+ let lang: Lang | undefined
96
+ if (/[cm]?tsx?/.test(ext)) {
97
+ lang = Lang.TypeScript
98
+ }
99
+ if (/[cm]?jsx?/.test(ext)) {
100
+ lang = Lang.JavaScript
101
+ }
102
+ if (/json?/.test(ext)) {
103
+ lang = Lang.Json
104
+ }
105
+ if (lang == null) return
106
+ const root = parse(lang, code).root()
107
+ allKeys.forEach(key => {
108
+ root
109
+ .findAll(key)
110
+ .forEach(node => {
111
+ const { start, end } = node.range()
112
+ const newValue = typeof allValues[key] === 'function'
113
+ ? allValues[key]({
114
+ ...ctx,
115
+ code,
116
+ mode: 'ast-grep',
117
+ node,
118
+ lang,
119
+ $: (input) => {
120
+ if (typeof input === 'string') {
121
+ return node.getMatch(input)?.text()
122
+ }
123
+ if ('raw' in input) {
124
+ return node.getMatch(input.raw[0])?.text()
125
+ }
126
+ }
127
+ })
128
+ : allValues[key]
129
+ if (([null, undefined, false] as unknown[]).includes(newValue)) return
130
+ ms.overwrite(
131
+ start.index,
132
+ end.index,
133
+ newValue as string
134
+ )
135
+ })
136
+ })
137
+ }
138
+ return ms
139
+ }
140
+
141
+ return {
142
+ name: 'jiek:replace',
143
+ buildStart() {
144
+ if (mode === 'ast-grep') {
145
+ try {
146
+ require.resolve('@ast-grep/napi')
147
+ this.warn(
148
+ 'You are using `ast-grep` mode, please make sure you have installed `@ast-grep/napi`'
149
+ )
150
+ } catch {
151
+ mode = 'string'
152
+ }
153
+ }
154
+ },
155
+ transform: {
156
+ order: 'pre',
157
+ async handler(code, id) {
158
+ if (allKeys.length === 0) return
159
+ if (filter(id)) return
160
+ const ms = await replaceAll({ type: 'transform', id }, code)
161
+ if (ms == null) return
162
+
163
+ return {
164
+ code: ms.toString(),
165
+ map: sourcemap ? ms.generateMap({ hires: true }) : null
166
+ }
167
+ }
168
+ },
169
+ renderChunk: {
170
+ order: 'post',
171
+ async handler(code, { fileName: id }) {
172
+ if (allKeys.length === 0) return
173
+ if (filter(id)) return
174
+
175
+ const ms = await replaceAll({ type: 'renderChunk', id }, code)
176
+ if (ms == null) return
177
+
178
+ return {
179
+ code: ms.toString(),
180
+ map: sourcemap ? ms.generateMap({ hires: true }) : null
181
+ }
182
+ }
183
+ }
184
+ }
185
+ })
@@ -0,0 +1,32 @@
1
+ import { isMatch } from 'micromatch'
2
+ import type { PluginImpl } from 'rollup'
3
+
4
+ export const definePlugin = <O extends object>(plugin: PluginImpl<O>) => plugin
5
+
6
+ export interface FilterOptions {
7
+ include?: string | RegExp | (string | RegExp)[]
8
+ exclude?: string | RegExp | (string | RegExp)[]
9
+ }
10
+
11
+ export function createFilter(options: FilterOptions) {
12
+ const { include = [], exclude = [] } = options
13
+
14
+ const resolvedInclude = Array.isArray(include) ? include : [include]
15
+ const resolvedExclude = Array.isArray(exclude) ? exclude : [exclude]
16
+
17
+ return (id: string) => {
18
+ if (typeof id !== 'string') return false
19
+ const isInclude = resolvedInclude.length === 0 || resolvedInclude.some(filter => {
20
+ return filter instanceof RegExp
21
+ ? filter.test(id)
22
+ : isMatch(id, filter)
23
+ })
24
+ const isExclude = resolvedExclude.length > 0 && resolvedExclude.some(filter => {
25
+ return filter instanceof RegExp
26
+ ? filter.test(id)
27
+ : isMatch(id, filter)
28
+ })
29
+
30
+ return !isInclude || isExclude
31
+ }
32
+ }