@jcoreio/toolchain 5.9.0 → 5.10.1

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcoreio/toolchain",
3
- "version": "5.9.0",
3
+ "version": "5.10.1",
4
4
  "description": "base JS build toolchain",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,6 +17,7 @@
17
17
  "@babel/generator": "^7.27.0",
18
18
  "@babel/parser": "^7.27.0",
19
19
  "@babel/template": "^7.27.0",
20
+ "@babel/traverse": "^7.26.4",
20
21
  "@babel/types": "^7.27.0",
21
22
  "@eslint/compat": "^1.2.8",
22
23
  "@eslint/js": "^9.23.0",
@@ -48,7 +48,6 @@ module.exports = [
48
48
  'toolchain.config.cjs': async (existing) => {
49
49
  if (existing) return existing
50
50
  return dedent`
51
- /* eslint-env node, es2018 */
52
51
  module.exports = {
53
52
  buildIgnore: ${JSON.stringify(await initBuildIgnore(), null, 2)},
54
53
  // scripts: {
@@ -69,7 +68,6 @@ module.exports = [
69
68
  )
70
69
  }
71
70
  return dedent`
72
- /* eslint-env node, es2018 */
73
71
  const base = require('${name}/prettierConfig.cjs')
74
72
  module.exports = {
75
73
  ...base,
@@ -84,7 +82,6 @@ module.exports = [
84
82
  files[file] = async (existing) =>
85
83
  existing && fromVersion ? existing : (
86
84
  dedent`
87
- /* eslint-env node, es2018 */
88
85
  const base = require('${name}/${file}')
89
86
  module.exports = {
90
87
  ...base,
@@ -6,6 +6,7 @@ const path = require('path')
6
6
  const fs = require('../util/projectFs.cjs')
7
7
  const { globSync } = require('../util/glob.cjs')
8
8
  const execa = require('../util/execa.cjs')
9
+ const globals = require('globals')
9
10
 
10
11
  module.exports = [
11
12
  () => {
@@ -21,7 +22,8 @@ module.exports = [
21
22
  if (globalGitignore && fs.pathExistsSync(globalGitignore)) {
22
23
  gitignores.push(globalGitignore)
23
24
  }
24
- } catch {
25
+ // eslint-disable-next-line no-unused-vars
26
+ } catch (err) {
25
27
  // ignore
26
28
  }
27
29
  if (fs.pathExistsSync('.eslintignore')) {
@@ -75,6 +77,17 @@ module.exports = [
75
77
  ],
76
78
  },
77
79
  },
80
+ {
81
+ files: ['**/*.{js,cjs,mjs,ts,cts,mts}'],
82
+ ignores: ['src/**', 'test/**'],
83
+ languageOptions: {
84
+ ecmaVersion: 2018,
85
+ globals: {
86
+ ...globals.es2018,
87
+ ...globals.node,
88
+ },
89
+ },
90
+ },
78
91
  require('eslint-config-prettier'),
79
92
  ])
80
93
  },
@@ -0,0 +1,120 @@
1
+ const { gte } = require('semver')
2
+
3
+ async function migrateEslintEnvComments({ fromVersion }) {
4
+ // istanbul ignore next
5
+ if (fromVersion && gte(fromVersion, '5.10.1')) {
6
+ return
7
+ }
8
+
9
+ const fs = require('../../util/projectFs.cjs')
10
+ const { glob } = require('../../util/glob.cjs')
11
+ const path = require('path')
12
+ const replaceRanges = require('../../util/replaceRanges.cjs')
13
+ const getBabelParseOpts = require('../../util/getBabelParseOpts.cjs')
14
+ const chalk = require('chalk')
15
+ const dedent = require('dedent-js')
16
+ const { parse } = require('@babel/parser')
17
+
18
+ /** env name -> array of paths from proj root to file */
19
+ const envFileMap = new Map()
20
+ const warnings = {}
21
+ for (const file of await glob('**/*.{js,jsx,cjs,mjs,ts,cts,mts,tsx}', {
22
+ dot: true,
23
+ })) {
24
+ function warn(warning) {
25
+ ;(warnings[file] || (warnings[file] = [])).push(warning)
26
+ }
27
+
28
+ const source = await fs.readFile(file, 'utf8')
29
+ let parsed
30
+ try {
31
+ parsed = parse(source, getBabelParseOpts(file))
32
+ } catch (error) {
33
+ warn(`parse error: ${error.message}`)
34
+ continue
35
+ }
36
+
37
+ if (!parsed || !('comments' in parsed) || !Array.isArray(parsed.comments)) {
38
+ continue
39
+ }
40
+
41
+ const replacements = []
42
+ for (const comment of parsed.comments) {
43
+ const { start, end, value } = comment
44
+ const match = /^\s*eslint-env\s+(.*?)\s*$/.exec(value)
45
+ const envs = match ? match[1].split(/\s*,\s*/g) : undefined
46
+ if (envs == null) continue
47
+ replacements.push({ start, end, value: '' })
48
+
49
+ if (file.startsWith('src/') || file.startsWith('test/')) {
50
+ for (const env of envs) {
51
+ let group = envFileMap.get(env)
52
+ if (!group) envFileMap.set(env, (group = []))
53
+ group.push(file)
54
+ }
55
+ }
56
+ }
57
+
58
+ if (!replacements.length) continue
59
+ await fs.writeFile(file, replaceRanges(source, replacements), 'utf8')
60
+ // eslint-disable-next-line no-console
61
+ console.error(
62
+ `removed eslint-env comments from ${path.relative(process.cwd(), file)}`
63
+ )
64
+ }
65
+
66
+ if (warnings.length) {
67
+ for (const [file, fileWarnings] of Object.entries(warnings)) {
68
+ // eslint-disable-next-line no-console
69
+ console.warn(
70
+ chalk.yellow(
71
+ dedent`
72
+ WARNING: ${file} could not be completely migrated because of the following:
73
+ ${fileWarnings.map((w) => `- ${w}`).join('\n ')}
74
+
75
+ `
76
+ )
77
+ )
78
+ }
79
+ }
80
+ if (!envFileMap.size) return
81
+
82
+ const t = require('@babel/types')
83
+ const addEslintConfigsCodemod = require('../../util/addEslintConfigsCodemod.cjs')
84
+
85
+ const newConfigs = [...envFileMap].map(([env, files]) =>
86
+ t.objectExpression([
87
+ t.objectProperty(
88
+ t.identifier('files'),
89
+ t.arrayExpression(files.map((file) => t.stringLiteral(file)))
90
+ ),
91
+ t.objectProperty(
92
+ t.identifier('languageOptions'),
93
+ t.objectExpression([
94
+ t.objectProperty(
95
+ t.identifier('globals'),
96
+ t.memberExpression(
97
+ t.identifier('globals'),
98
+ /^[_a-z$][_a-z0-9$]*$/.test(env) ?
99
+ t.identifier(env)
100
+ : t.stringLiteral(env)
101
+ )
102
+ ),
103
+ ])
104
+ ),
105
+ ])
106
+ )
107
+
108
+ await fs.writeFile(
109
+ 'eslint.config.cjs',
110
+ await addEslintConfigsCodemod({
111
+ file: 'eslint.config.cjs',
112
+ source: await fs.readFile('eslint.config.cjs', 'utf8'),
113
+ configs: newConfigs,
114
+ requireGlobals: true,
115
+ }),
116
+ 'utf8'
117
+ )
118
+ }
119
+
120
+ module.exports = migrateEslintEnvComments
@@ -14,6 +14,7 @@ async function migrate(args = []) {
14
14
  const installGitHooks = require('./install-git-hooks.cjs')
15
15
  const migrateProjectPackageJson = require('./migrate/migrateProjectPackageJson.cjs')
16
16
  const migrateEslintConfigs = require('./migrate/migrateEslintConfigs.cjs')
17
+ const migrateEslintEnvComments = require('./migrate/migrateEslintEnvComments.cjs')
17
18
  const migrateConfigFiles = require('./migrate/migrateConfigFiles.cjs')
18
19
  const migrateMoveTypeDefs = require('./migrate/migrateMoveTypeDefs.cjs')
19
20
  const migrateGitignore = require('./migrate/migrateGitignore.cjs')
@@ -32,6 +33,7 @@ async function migrate(args = []) {
32
33
  }
33
34
  await migrateConfigFiles({ fromVersion })
34
35
  await migrateEslintConfigs({ fromVersion })
36
+ await migrateEslintEnvComments({ fromVersion })
35
37
  if (!fromVersion) await migrateMoveTypeDefs()
36
38
  await migrateGitignore({ fromVersion })
37
39
  await getPluginsAsyncFunction('migrate')(args, { fromVersion })
@@ -0,0 +1,73 @@
1
+ const traverse = require('@babel/traverse').default
2
+ const { generate } = require('@babel/generator')
3
+ const { parse } = require('@babel/parser')
4
+ const getBabelParseOpts = require('./getBabelParseOpts.cjs')
5
+ const { format } = require('prettier')
6
+ const prettierConfig = require('../prettierConfig.cjs')
7
+ const replaceRanges = require('./replaceRanges.cjs')
8
+
9
+ async function addEslintConfigsCodemod({
10
+ file,
11
+ source,
12
+ configs,
13
+ requireGlobals,
14
+ }) {
15
+ const ast = parse(source, getBabelParseOpts(file))
16
+ const replacements = []
17
+
18
+ let needsRequireGlobals = requireGlobals
19
+
20
+ traverse(ast, {
21
+ VariableDeclarator(path) {
22
+ const { id, init } = path.node
23
+ if (
24
+ id.type === 'Identifier' &&
25
+ id.name === 'globals' &&
26
+ init.type === 'CallExpression' &&
27
+ init.callee.type === 'Identifier' &&
28
+ init.callee.name === 'require' &&
29
+ init.arguments.length === 1 &&
30
+ init.arguments[0].type === 'StringLiteral' &&
31
+ init.arguments[0].value === 'globals'
32
+ ) {
33
+ needsRequireGlobals = false
34
+ }
35
+ },
36
+ CallExpression(path) {
37
+ const { callee, arguments: args } = path.node
38
+ const arg0 = args ? args[0] : undefined
39
+ if (
40
+ callee.type !== 'Identifier' ||
41
+ callee.name !== 'defineConfig' ||
42
+ arg0 == null ||
43
+ arg0.type !== 'ArrayExpression'
44
+ ) {
45
+ return
46
+ }
47
+
48
+ const { start, end } = arg0
49
+ const needsComma = !/,\s*\]\s*$/.test(source.substring(start, end))
50
+ replacements.push({
51
+ start: end - 1,
52
+ end: end - 1,
53
+ value:
54
+ (needsComma ? ',' : '') +
55
+ configs.map((config) => generate(config).code).join(','),
56
+ })
57
+ },
58
+ })
59
+
60
+ if (needsRequireGlobals) {
61
+ replacements.push({
62
+ start: 0,
63
+ end: 0,
64
+ value: `const globals = require('globals')\n`,
65
+ })
66
+ }
67
+
68
+ return await format(replaceRanges(source, replacements), {
69
+ ...prettierConfig,
70
+ parser: 'babel',
71
+ })
72
+ }
73
+ module.exports = addEslintConfigsCodemod
package/util/findUps.cjs CHANGED
@@ -17,7 +17,8 @@ function matchNamedPackageJson(directory) {
17
17
  try {
18
18
  const json = fs.readJsonSync(Path.join(directory, 'package.json'))
19
19
  if (json.name) return 'package.json'
20
- } catch {
20
+ // eslint-disable-next-line no-unused-vars
21
+ } catch (err) {
21
22
  return undefined
22
23
  }
23
24
  }
@@ -0,0 +1,151 @@
1
+ const commonPlugins = [
2
+ 'asyncGenerators',
3
+ 'bigInt',
4
+ 'classProperties',
5
+ 'classPrivateProperties',
6
+ 'classPrivateMethods',
7
+ 'classStaticBlock',
8
+ 'functionSent',
9
+ 'logicalAssignment',
10
+ 'moduleStringNames',
11
+ 'nullishCoalescingOperator',
12
+ 'numericSeparator',
13
+ 'objectRestSpread',
14
+ 'optionalCatchBinding',
15
+ 'optionalChaining',
16
+ 'privateIn',
17
+ ]
18
+
19
+ const modulePlugins = [
20
+ 'dynamicImport',
21
+ 'exportNamespaceFrom',
22
+ 'exportDefaultFrom',
23
+ 'importMeta',
24
+ 'topLevelAwait',
25
+ ]
26
+
27
+ const babelParseOpts = {
28
+ js: {
29
+ sourceType: 'unambiguous',
30
+ allowImportExportEverywhere: true,
31
+ allowReturnOutsideFunction: true,
32
+ startLine: 1,
33
+ plugins: [
34
+ ...commonPlugins,
35
+ ...modulePlugins,
36
+ ['flow', { all: true }],
37
+ 'flowComments',
38
+ 'jsx',
39
+ ],
40
+ },
41
+ jsx: {
42
+ sourceType: 'unambiguous',
43
+ allowImportExportEverywhere: true,
44
+ allowReturnOutsideFunction: true,
45
+ startLine: 1,
46
+ plugins: [
47
+ ...commonPlugins,
48
+ ...modulePlugins,
49
+ ['flow', { all: true }],
50
+ 'flowComments',
51
+ 'jsx',
52
+ ],
53
+ },
54
+ cjs: {
55
+ sourceType: 'commonjs',
56
+ allowReturnOutsideFunction: true,
57
+ startLine: 1,
58
+ },
59
+ mjs: {
60
+ sourceType: 'module',
61
+ allowImportExportEverywhere: true,
62
+ allowReturnOutsideFunction: true,
63
+ startLine: 1,
64
+ plugins: [...commonPlugins, ...modulePlugins],
65
+ },
66
+ ts: {
67
+ sourceType: 'unambiguous',
68
+ allowImportExportEverywhere: true,
69
+ allowReturnOutsideFunction: true,
70
+ startLine: 1,
71
+ plugins: [
72
+ ...commonPlugins,
73
+ ...modulePlugins,
74
+ 'typescript',
75
+ 'decorators-legacy',
76
+ ],
77
+ },
78
+ 'd.ts': {
79
+ sourceType: 'unambiguous',
80
+ allowImportExportEverywhere: true,
81
+ allowReturnOutsideFunction: true,
82
+ startLine: 1,
83
+ plugins: [
84
+ ...commonPlugins,
85
+ ...modulePlugins,
86
+ ['typescript', { dts: true }],
87
+ 'decorators-legacy',
88
+ ],
89
+ },
90
+ tsx: {
91
+ sourceType: 'unambiguous',
92
+ allowImportExportEverywhere: true,
93
+ allowReturnOutsideFunction: true,
94
+ startLine: 1,
95
+ plugins: [
96
+ ...commonPlugins,
97
+ ...modulePlugins,
98
+ 'jsx',
99
+ 'typescript',
100
+ 'decorators-legacy',
101
+ ],
102
+ },
103
+ cts: {
104
+ sourceType: 'commonjs',
105
+ allowReturnOutsideFunction: true,
106
+ startLine: 1,
107
+ plugins: [...commonPlugins, 'typescript', 'decorators-legacy'],
108
+ },
109
+ 'd.cts': {
110
+ sourceType: 'commonjs',
111
+ allowReturnOutsideFunction: true,
112
+ startLine: 1,
113
+ plugins: [
114
+ ...commonPlugins,
115
+ ['typescript', { dts: true }],
116
+ 'decorators-legacy',
117
+ ],
118
+ },
119
+ mts: {
120
+ sourceType: 'module',
121
+ allowImportExportEverywhere: true,
122
+ allowReturnOutsideFunction: true,
123
+ startLine: 1,
124
+ plugins: [
125
+ ...commonPlugins,
126
+ ...modulePlugins,
127
+ 'typescript',
128
+ 'decorators-legacy',
129
+ ],
130
+ },
131
+ 'd.mts': {
132
+ sourceType: 'module',
133
+ allowImportExportEverywhere: true,
134
+ allowReturnOutsideFunction: true,
135
+ startLine: 1,
136
+ plugins: [
137
+ ...commonPlugins,
138
+ ...modulePlugins,
139
+ ['typescript', { dts: true }],
140
+ 'decorators-legacy',
141
+ ],
142
+ },
143
+ }
144
+
145
+ function getBabelParseOpts(file) {
146
+ const match = /\.(js|cjs|mjs|jsx|tsx|(d\.)?(ts|cts|mts))$/i.exec(file)
147
+ const ext = (match ? match[1] : undefined) || 'js'
148
+ return babelParseOpts[ext]
149
+ }
150
+
151
+ module.exports = getBabelParseOpts