rootless-config 1.2.0 → 1.3.0
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 +1 -1
- package/src/cli/commands/migrate.js +23 -15
- package/src/core/scriptPatcher.js +181 -82
package/package.json
CHANGED
|
@@ -5,22 +5,29 @@ import { readdir, unlink, readFile } from 'node:fs/promises'
|
|
|
5
5
|
import { createLogger } from '../../utils/logger.js'
|
|
6
6
|
import { fileExists, ensureDir, atomicWrite, readJsonFile } from '../../utils/fsUtils.js'
|
|
7
7
|
import { confirm } from '../../utils/prompt.js'
|
|
8
|
-
import { patchPackageScripts } from '../../core/scriptPatcher.js'
|
|
8
|
+
import { patchPackageScripts, getAllKnownFiles, NEVER_MIGRATE } from '../../core/scriptPatcher.js'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const ENV_PATTERN
|
|
12
|
-
|
|
13
|
-
const
|
|
10
|
+
// Pattern-based detection for file families not covered by the explicit known-files set
|
|
11
|
+
const ENV_PATTERN = /^\.env/ // .env, .env.local, .env.production, etc.
|
|
12
|
+
const TSCONFIG_PATTERN = /^tsconfig.*\.json$/ // tsconfig.custom.json etc.
|
|
13
|
+
const DOCKERFILE_PATTERN = /^Dockerfile/ // Dockerfile.dev, Dockerfile.prod etc.
|
|
14
|
+
const DOCKER_COMPOSE_PATTERN = /^docker-compose/ // docker-compose.override.yml etc.
|
|
14
15
|
|
|
15
16
|
async function findMigratableFiles(projectRoot) {
|
|
17
|
+
const knownFiles = getAllKnownFiles()
|
|
16
18
|
const entries = await readdir(projectRoot, { withFileTypes: true })
|
|
17
19
|
return entries
|
|
18
|
-
.filter(e =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
.filter(e => {
|
|
21
|
+
if (!e.isFile()) return false
|
|
22
|
+
if (NEVER_MIGRATE.has(e.name)) return false
|
|
23
|
+
return (
|
|
24
|
+
knownFiles.has(e.name) ||
|
|
25
|
+
ENV_PATTERN.test(e.name) ||
|
|
26
|
+
TSCONFIG_PATTERN.test(e.name) ||
|
|
27
|
+
DOCKERFILE_PATTERN.test(e.name) ||
|
|
28
|
+
DOCKER_COMPOSE_PATTERN.test(e.name)
|
|
29
|
+
)
|
|
30
|
+
})
|
|
24
31
|
.map(e => e.name)
|
|
25
32
|
.sort()
|
|
26
33
|
}
|
|
@@ -68,18 +75,19 @@ export default {
|
|
|
68
75
|
for (const name of candidates) {
|
|
69
76
|
const src = path.join(projectRoot, name)
|
|
70
77
|
const isEnv = ENV_PATTERN.test(name)
|
|
71
|
-
const
|
|
78
|
+
const isAsset = /\.(ico|png|jpg|jpeg|svg|txt|xml|webmanifest|json)$/.test(name) &&
|
|
79
|
+
['favicon.ico', 'robots.txt', 'sitemap.xml', 'manifest.json', 'site.webmanifest'].includes(name)
|
|
80
|
+
const destSubdir = isEnv ? 'env' : (isAsset ? 'assets' : 'configs')
|
|
81
|
+
const destDir = path.join(containerPath, destSubdir)
|
|
72
82
|
|
|
73
83
|
await ensureDir(destDir)
|
|
74
84
|
const content = await readFile(src, 'utf8')
|
|
75
85
|
await atomicWrite(path.join(destDir, name), content)
|
|
76
86
|
|
|
77
87
|
if (isCleanMode) {
|
|
78
|
-
// Clean mode: delete original — no proxy file in root
|
|
79
88
|
await unlink(src)
|
|
80
|
-
logger.success(`Moved to .root/${
|
|
89
|
+
logger.success(`Moved to .root/${destSubdir}/${name} (deleted from root)`)
|
|
81
90
|
} else {
|
|
82
|
-
// Proxy mode: replace original with re-export stub
|
|
83
91
|
const rel = path.relative(path.dirname(src), path.join(destDir, name)).replace(/\\/g, '/')
|
|
84
92
|
const relPath = rel.startsWith('.') ? rel : `./${rel}`
|
|
85
93
|
await atomicWrite(src, `export { default } from "${relPath}"\n`)
|
|
@@ -1,92 +1,190 @@
|
|
|
1
|
-
/*-------- Patches package.json scripts to use --config flags pointing to .root/ --------*/
|
|
1
|
+
/*-------- Patches package.json scripts to use --config flags pointing to .root/ --------*/
|
|
2
2
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { readdir } from 'node:fs/promises'
|
|
5
5
|
import { readJsonFile, writeJsonFile, fileExists } from '../utils/fsUtils.js'
|
|
6
6
|
|
|
7
|
+
// ─── Tool definitions ────────────────────────────────────────────────────────
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Tools that support a --config (or similar) flag.
|
|
9
|
-
*
|
|
10
|
-
* Ordered by specificity (longer
|
|
11
|
+
* Matching files will NOT be copied to root — scripts get patched instead.
|
|
12
|
+
* Ordered by command specificity (longer commands first to avoid partial matches).
|
|
11
13
|
*/
|
|
12
14
|
const TOOL_CONFIGS = [
|
|
13
|
-
|
|
14
|
-
{ cmd: 'vite
|
|
15
|
-
{ cmd: 'vite',
|
|
16
|
-
{ cmd: '
|
|
17
|
-
|
|
18
|
-
{ cmd: '
|
|
19
|
-
{ cmd: '
|
|
20
|
-
|
|
21
|
-
{ cmd: '
|
|
22
|
-
|
|
23
|
-
{ cmd: '
|
|
24
|
-
|
|
25
|
-
{ cmd: '
|
|
26
|
-
|
|
27
|
-
{ cmd: '
|
|
28
|
-
|
|
29
|
-
{ cmd: '
|
|
30
|
-
{ cmd: '
|
|
31
|
-
|
|
32
|
-
{ cmd: '
|
|
33
|
-
|
|
34
|
-
{ cmd: '
|
|
35
|
-
|
|
36
|
-
{ cmd: '
|
|
15
|
+
// Vite
|
|
16
|
+
{ cmd: 'vite build', flag: '--config', files: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'] },
|
|
17
|
+
{ cmd: 'vite preview', flag: '--config', files: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'] },
|
|
18
|
+
{ cmd: 'vite', flag: '--config', files: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs'] },
|
|
19
|
+
// Vitest
|
|
20
|
+
{ cmd: 'vitest run', flag: '--config', files: ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'] },
|
|
21
|
+
{ cmd: 'vitest', flag: '--config', files: ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'] },
|
|
22
|
+
// ESLint (v8 flat config + legacy .eslintrc variants)
|
|
23
|
+
{ cmd: 'eslint', flag: '--config', files: ['eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs', '.eslintrc', '.eslintrc.js', '.eslintrc.cjs', '.eslintrc.mjs', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml'] },
|
|
24
|
+
// Prettier
|
|
25
|
+
{ cmd: 'prettier', flag: '--config', files: ['prettier.config.js', 'prettier.config.cjs', 'prettier.config.mjs', '.prettierrc', '.prettierrc.js', '.prettierrc.cjs', '.prettierrc.json', '.prettierrc.yaml', '.prettierrc.yml'] },
|
|
26
|
+
// Stylelint
|
|
27
|
+
{ cmd: 'stylelint', flag: '--config', files: ['stylelint.config.js', 'stylelint.config.cjs', 'stylelint.config.mjs', '.stylelintrc', '.stylelintrc.js', '.stylelintrc.json', '.stylelintrc.yaml', '.stylelintrc.yml'] },
|
|
28
|
+
// Jest
|
|
29
|
+
{ cmd: 'jest', flag: '--config', files: ['jest.config.js', 'jest.config.cjs', 'jest.config.mjs', 'jest.config.ts', 'jest.config.json'] },
|
|
30
|
+
// Webpack
|
|
31
|
+
{ cmd: 'webpack serve', flag: '--config', files: ['webpack.config.js', 'webpack.config.cjs', 'webpack.config.mjs', 'webpack.config.ts', 'webpack.common.js', 'webpack.dev.js', 'webpack.prod.js', 'webpack.base.js', 'webpack.config.babel.js'] },
|
|
32
|
+
{ cmd: 'webpack', flag: '--config', files: ['webpack.config.js', 'webpack.config.cjs', 'webpack.config.mjs', 'webpack.config.ts', 'webpack.common.js', 'webpack.dev.js', 'webpack.prod.js', 'webpack.base.js', 'webpack.config.babel.js'] },
|
|
33
|
+
// Rollup
|
|
34
|
+
{ cmd: 'rollup', flag: '--config', files: ['rollup.config.js', 'rollup.config.mjs', 'rollup.config.cjs', 'rollup.config.ts'] },
|
|
35
|
+
// tsup
|
|
36
|
+
{ cmd: 'tsup', flag: '--config', files: ['tsup.config.js', 'tsup.config.ts', 'tsup.config.mjs'] },
|
|
37
|
+
// TypeScript compiler
|
|
38
|
+
{ cmd: 'tsc', flag: '--project', files: ['tsconfig.json', 'tsconfig.base.json', 'tsconfig.app.json', 'tsconfig.build.json', 'tsconfig.node.json', 'tsconfig.spec.json', 'tsconfig.test.json', 'tsconfig.worker.json'] },
|
|
39
|
+
// Nodemon
|
|
40
|
+
{ cmd: 'nodemon', flag: '--config', files: ['nodemon.json', '.nodemonrc', 'nodemon.config.js'] },
|
|
41
|
+
// Babel
|
|
42
|
+
{ cmd: 'babel', flag: '--config-file', files: ['babel.config.js', 'babel.config.cjs', 'babel.config.mjs', 'babel.config.json', '.babelrc', '.babelrc.js', '.babelrc.cjs', '.babelrc.json'] },
|
|
43
|
+
// PostCSS
|
|
44
|
+
{ cmd: 'postcss', flag: '--config', files: ['postcss.config.js', 'postcss.config.cjs', 'postcss.config.mjs', 'postcss.config.ts'] },
|
|
45
|
+
// Tailwind CSS
|
|
46
|
+
{ cmd: 'tailwindcss', flag: '--config', files: ['tailwind.config.js', 'tailwind.config.cjs', 'tailwind.config.mjs', 'tailwind.config.ts'] },
|
|
47
|
+
// Mocha
|
|
48
|
+
{ cmd: 'mocha', flag: '--config', files: ['.mocharc.js', '.mocharc.cjs', '.mocharc.json', '.mocharc.yaml', '.mocharc.yml'] },
|
|
49
|
+
// nyc (Istanbul coverage)
|
|
50
|
+
{ cmd: 'nyc', flag: '--nycrc', files: ['.nycrc', '.nycrc.json', 'nyc.config.js', 'nyc.config.cjs'] },
|
|
51
|
+
// Cypress
|
|
52
|
+
{ cmd: 'cypress run', flag: '--config-file', files: ['cypress.config.js', 'cypress.config.ts'] },
|
|
53
|
+
{ cmd: 'cypress open', flag: '--config-file', files: ['cypress.config.js', 'cypress.config.ts'] },
|
|
54
|
+
// Playwright
|
|
55
|
+
{ cmd: 'playwright test', flag: '--config', files: ['playwright.config.js', 'playwright.config.ts'] },
|
|
56
|
+
// Astro
|
|
57
|
+
{ cmd: 'astro dev', flag: '--config', files: ['astro.config.js', 'astro.config.mjs', 'astro.config.ts'] },
|
|
58
|
+
{ cmd: 'astro build', flag: '--config', files: ['astro.config.js', 'astro.config.mjs', 'astro.config.ts'] },
|
|
59
|
+
{ cmd: 'astro preview', flag: '--config', files: ['astro.config.js', 'astro.config.mjs', 'astro.config.ts'] },
|
|
60
|
+
{ cmd: 'astro', flag: '--config', files: ['astro.config.js', 'astro.config.mjs', 'astro.config.ts'] },
|
|
61
|
+
// Biome
|
|
62
|
+
{ cmd: 'biome check', flag: '--config-path', files: ['biome.json'] },
|
|
63
|
+
{ cmd: 'biome lint', flag: '--config-path', files: ['biome.json'] },
|
|
64
|
+
{ cmd: 'biome format', flag: '--config-path', files: ['biome.json'] },
|
|
65
|
+
{ cmd: 'biome ci', flag: '--config-path', files: ['biome.json'] },
|
|
66
|
+
{ cmd: 'biome', flag: '--config-path', files: ['biome.json'] },
|
|
37
67
|
]
|
|
38
68
|
|
|
39
69
|
/**
|
|
40
70
|
* Tools that use the config file as a POSITIONAL argument (not a --flag).
|
|
41
|
-
* These files can also be removed from root — the argument in the script is patched.
|
|
42
71
|
*/
|
|
43
72
|
const POSITIONAL_TOOLS = [
|
|
44
|
-
{ cmd: 'pm2 start', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs'] },
|
|
45
|
-
{ cmd: 'pm2 restart', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs'] },
|
|
46
|
-
{ cmd: 'pm2 reload', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs'] },
|
|
73
|
+
{ cmd: 'pm2 start', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs', 'pm2.config.js'] },
|
|
74
|
+
{ cmd: 'pm2 restart', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs', 'pm2.config.js'] },
|
|
75
|
+
{ cmd: 'pm2 reload', files: ['ecosystem.config.js', 'ecosystem.config.cjs', 'ecosystem.config.mjs', 'pm2.config.js'] },
|
|
76
|
+
{ cmd: 'karma start', files: ['karma.conf.js', 'karma.conf.ts'] },
|
|
77
|
+
{ cmd: 'karma', files: ['karma.conf.js', 'karma.conf.ts'] },
|
|
47
78
|
]
|
|
48
79
|
|
|
49
80
|
/**
|
|
50
81
|
* Files that MUST be physically present in the project root.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* but we keep an explicit list for clarity and migrate detection.
|
|
82
|
+
* Tools discover them by convention — no CLI redirect is possible.
|
|
83
|
+
* These are COPIED to root by `rootless prepare`.
|
|
54
84
|
*/
|
|
55
85
|
const ROOT_REQUIRED_FILES = new Set([
|
|
56
|
-
// Node
|
|
86
|
+
// Node / npm
|
|
87
|
+
'.npmrc',
|
|
88
|
+
// Node version managers
|
|
57
89
|
'.nvmrc', '.node-version',
|
|
58
90
|
// Package managers
|
|
59
|
-
'.
|
|
91
|
+
'.yarnrc', '.yarnrc.yml', '.pnpmfile.cjs', 'pnpm-workspace.yaml',
|
|
92
|
+
// TypeScript (auto-discovered by ts-node, ts-jest, esbuild, IDEs, etc.)
|
|
93
|
+
'tsconfig.json', 'tsconfig.base.json', 'tsconfig.app.json', 'tsconfig.build.json',
|
|
94
|
+
'tsconfig.node.json', 'tsconfig.spec.json', 'tsconfig.test.json', 'tsconfig.worker.json',
|
|
95
|
+
'jsconfig.json',
|
|
60
96
|
// Editor
|
|
61
97
|
'.editorconfig',
|
|
62
|
-
// Git
|
|
63
|
-
'.gitignore', '.gitattributes', '.gitmodules',
|
|
64
|
-
//
|
|
65
|
-
'
|
|
66
|
-
//
|
|
67
|
-
'
|
|
98
|
+
// Git
|
|
99
|
+
'.gitignore', '.gitattributes', '.gitmodules', '.mailmap',
|
|
100
|
+
// Linting ignore files (auto-discovered, no --flag support)
|
|
101
|
+
'.eslintignore', '.prettierignore', '.stylelintignore',
|
|
102
|
+
// Parcel (auto-discovers .parcelrc from root)
|
|
103
|
+
'.parcelrc', 'parcel.config.js',
|
|
104
|
+
// Next.js (no --config flag support)
|
|
105
|
+
'next.config.js', 'next.config.mjs', 'next.config.ts',
|
|
106
|
+
'middleware.ts', 'middleware.js', 'instrumentation.ts', 'instrumentation.js',
|
|
107
|
+
// Nuxt (auto-discovered)
|
|
108
|
+
'nuxt.config.js', 'nuxt.config.ts',
|
|
109
|
+
// Vue CLI (auto-discovered)
|
|
110
|
+
'vue.config.js',
|
|
111
|
+
// Angular
|
|
112
|
+
'angular.json', 'proxy.conf.json',
|
|
113
|
+
// SvelteKit / Svelte (auto-discovered by Vite plugin from root)
|
|
114
|
+
'svelte.config.js', 'svelte.config.cjs', 'svelte.config.mjs',
|
|
115
|
+
// Remix (auto-discovered)
|
|
116
|
+
'remix.config.js', 'remix.config.mjs', 'remix.config.ts',
|
|
117
|
+
// Monorepo tools
|
|
118
|
+
'turbo.json', 'nx.json', 'workspace.json', 'project.json', 'lerna.json', 'rush.json',
|
|
119
|
+
// Build tools (auto-discovered from root)
|
|
120
|
+
'.swcrc', 'swc.config.js', 'rome.json',
|
|
121
|
+
// CSS tooling (auto-discovered by framework plugins)
|
|
122
|
+
'windicss.config.js', 'windicss.config.ts', 'unocss.config.js', 'unocss.config.ts',
|
|
68
123
|
// Browser targets
|
|
69
124
|
'.browserslistrc', 'browserslist',
|
|
70
|
-
//
|
|
71
|
-
'.
|
|
72
|
-
// Commitlint / lint-staged / release (auto-discovery only)
|
|
125
|
+
// Commit / git hooks
|
|
126
|
+
'.huskyrc', '.huskyrc.json', '.huskyrc.js', '.huskyrc.yaml', '.huskyrc.yml',
|
|
73
127
|
'commitlint.config.js', 'commitlint.config.cjs', 'commitlint.config.mjs',
|
|
74
|
-
'.commitlintrc', '.commitlintrc.js', '.commitlintrc.json',
|
|
128
|
+
'.commitlintrc', '.commitlintrc.js', '.commitlintrc.json', '.commitlintrc.yaml', '.commitlintrc.yml',
|
|
129
|
+
'lint-staged.config.js', 'lint-staged.config.cjs', 'lint-staged.config.mjs',
|
|
75
130
|
'.lintstagedrc', '.lintstagedrc.js', '.lintstagedrc.json', '.lintstagedrc.cjs',
|
|
76
|
-
|
|
77
|
-
'.
|
|
78
|
-
'
|
|
79
|
-
// Semantic
|
|
131
|
+
// Release automation
|
|
132
|
+
'release.config.js', 'release.config.cjs', 'semantic-release.config.js', '.semantic-release.json',
|
|
133
|
+
'.releaserc', '.releaserc.js', '.releaserc.json', '.releaserc.yml', '.releaserc.yaml',
|
|
134
|
+
// Semantic commit
|
|
80
135
|
'.czrc', '.cz.json',
|
|
81
|
-
//
|
|
82
|
-
'.
|
|
83
|
-
//
|
|
136
|
+
// Docker
|
|
137
|
+
'Dockerfile', '.dockerignore', 'docker-compose.yml', 'docker-compose.yaml',
|
|
138
|
+
// CI
|
|
139
|
+
'.gitlab-ci.yml', 'azure-pipelines.yml',
|
|
140
|
+
// Web assets (served directly from root by web servers)
|
|
84
141
|
'robots.txt', 'sitemap.xml', 'favicon.ico', 'manifest.json', 'site.webmanifest',
|
|
142
|
+
// Documentation
|
|
143
|
+
'README.md', 'LICENSE', 'CHANGELOG.md', 'CONTRIBUTING.md', 'CODE_OF_CONDUCT.md',
|
|
144
|
+
'SECURITY.md', 'SUPPORT.md',
|
|
145
|
+
// Dependency management / update bots
|
|
146
|
+
'renovate.json', '.renovaterc', '.renovaterc.json',
|
|
147
|
+
// Misc
|
|
148
|
+
'.htaccess', 'Procfile',
|
|
85
149
|
])
|
|
86
150
|
|
|
87
151
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
152
|
+
* Lock files and core manifests that should NEVER be migrated.
|
|
153
|
+
* They must always live in the project root and are managed by package managers.
|
|
154
|
+
*/
|
|
155
|
+
const NEVER_MIGRATE = new Set([
|
|
156
|
+
'package.json',
|
|
157
|
+
'package-lock.json',
|
|
158
|
+
'yarn.lock',
|
|
159
|
+
'pnpm-lock.yaml',
|
|
160
|
+
'npm-shrinkwrap.json',
|
|
161
|
+
])
|
|
162
|
+
|
|
163
|
+
// ─── Helper functions ────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Returns all config file names known to rootless across all categories.
|
|
167
|
+
* Excludes NEVER_MIGRATE entries. Used by the migrate command.
|
|
168
|
+
*/
|
|
169
|
+
function getAllKnownFiles() {
|
|
170
|
+
const all = new Set()
|
|
171
|
+
for (const tool of TOOL_CONFIGS) {
|
|
172
|
+
for (const f of tool.files) all.add(f)
|
|
173
|
+
}
|
|
174
|
+
for (const tool of POSITIONAL_TOOLS) {
|
|
175
|
+
for (const f of tool.files) all.add(f)
|
|
176
|
+
}
|
|
177
|
+
for (const f of ROOT_REQUIRED_FILES) {
|
|
178
|
+
all.add(f)
|
|
179
|
+
}
|
|
180
|
+
for (const f of NEVER_MIGRATE) {
|
|
181
|
+
all.delete(f)
|
|
182
|
+
}
|
|
183
|
+
return all
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Returns true if the file can be redirected via CLI flags (not copied to root).
|
|
90
188
|
*/
|
|
91
189
|
function isPatchable(filename) {
|
|
92
190
|
return (
|
|
@@ -96,23 +194,23 @@ function isPatchable(filename) {
|
|
|
96
194
|
}
|
|
97
195
|
|
|
98
196
|
/**
|
|
99
|
-
* Returns true if the file must
|
|
197
|
+
* Returns true if the file must be physically present in the project root.
|
|
100
198
|
*/
|
|
101
199
|
function isRootRequired(filename) {
|
|
200
|
+
if (NEVER_MIGRATE.has(filename)) return false
|
|
102
201
|
if (ROOT_REQUIRED_FILES.has(filename)) return true
|
|
103
|
-
// .env
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
if (/^
|
|
107
|
-
|
|
108
|
-
if (!isPatchable(filename)) return true
|
|
202
|
+
if (/^\.env/.test(filename)) return true // .env.local, .env.production, etc.
|
|
203
|
+
if (/^Dockerfile/.test(filename)) return true // Dockerfile.dev, Dockerfile.prod etc.
|
|
204
|
+
if (/^tsconfig.*\.json$/.test(filename)) return true // tsconfig.custom.json variants
|
|
205
|
+
if (/^docker-compose/.test(filename)) return true // docker-compose.override.yml etc.
|
|
206
|
+
if (!isPatchable(filename)) return true // unknown file → copy to root to be safe
|
|
109
207
|
return false
|
|
110
208
|
}
|
|
111
209
|
|
|
210
|
+
// ─── Config map building ─────────────────────────────────────────────────────
|
|
211
|
+
|
|
112
212
|
/**
|
|
113
|
-
* Build a map of { cmd →
|
|
114
|
-
* configsDir is the absolute path to .root/configs/
|
|
115
|
-
* projectRoot is used to compute relative paths in scripts
|
|
213
|
+
* Build a map of { cmd → { path, flag, positional } } based on files in configsDir.
|
|
116
214
|
*/
|
|
117
215
|
async function buildConfigMap(configsDir, projectRoot) {
|
|
118
216
|
let files = []
|
|
@@ -124,10 +222,8 @@ async function buildConfigMap(configsDir, projectRoot) {
|
|
|
124
222
|
|
|
125
223
|
const map = {}
|
|
126
224
|
|
|
127
|
-
// Flag-based tools
|
|
128
225
|
for (const tool of TOOL_CONFIGS) {
|
|
129
|
-
if (!tool.files.length) continue
|
|
130
|
-
if (map[tool.cmd]) continue
|
|
226
|
+
if (!tool.files.length || map[tool.cmd]) continue
|
|
131
227
|
const found = tool.files.find(f => files.includes(f))
|
|
132
228
|
if (!found) continue
|
|
133
229
|
const abs = path.join(configsDir, found)
|
|
@@ -135,39 +231,36 @@ async function buildConfigMap(configsDir, projectRoot) {
|
|
|
135
231
|
map[tool.cmd] = { path: rel.startsWith('.') ? rel : `./${rel}`, flag: tool.flag, positional: false }
|
|
136
232
|
}
|
|
137
233
|
|
|
138
|
-
// Positional tools (pm2 etc.)
|
|
139
234
|
for (const tool of POSITIONAL_TOOLS) {
|
|
140
235
|
if (map[tool.cmd]) continue
|
|
141
236
|
const found = tool.files.find(f => files.includes(f))
|
|
142
237
|
if (!found) continue
|
|
143
238
|
const abs = path.join(configsDir, found)
|
|
144
239
|
const rel = path.relative(projectRoot, abs).replace(/\\/g, '/')
|
|
145
|
-
map[tool.cmd] = { path: rel.startsWith('.') ? rel : `./${rel}`, flag: null, positional: true,
|
|
240
|
+
map[tool.cmd] = { path: rel.startsWith('.') ? rel : `./${rel}`, flag: null, positional: true, originalFiles: tool.files }
|
|
146
241
|
}
|
|
147
242
|
|
|
148
243
|
return map
|
|
149
244
|
}
|
|
150
245
|
|
|
246
|
+
// ─── Script patching ─────────────────────────────────────────────────────────
|
|
247
|
+
|
|
151
248
|
/**
|
|
152
|
-
* Inject --config flag
|
|
153
|
-
* Handles semicolons, &&, || (multi-command scripts).
|
|
249
|
+
* Inject --config flag (or replace positional arg) in a single script string.
|
|
154
250
|
*/
|
|
155
251
|
function patchScriptString(script, configMap) {
|
|
156
|
-
// Sort by command length descending to avoid partial matches
|
|
157
252
|
const sorted = Object.entries(configMap).sort((a, b) => b[0].length - a[0].length)
|
|
158
253
|
|
|
159
254
|
let result = script
|
|
160
255
|
for (const [cmd, info] of sorted) {
|
|
161
256
|
if (info.positional) {
|
|
162
|
-
|
|
163
|
-
for (const file of (POSITIONAL_TOOLS.find(t => t.cmd === cmd)?.files ?? [])) {
|
|
257
|
+
for (const file of (info.originalFiles ?? [])) {
|
|
164
258
|
const escapedFile = file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
165
259
|
const escapedCmd = cmd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+')
|
|
166
260
|
const regex = new RegExp(`(${escapedCmd})\\s+${escapedFile}`, 'g')
|
|
167
261
|
result = result.replace(regex, `$1 ${info.path}`)
|
|
168
262
|
}
|
|
169
263
|
} else {
|
|
170
|
-
// Flag-based: insert --config <path> after command if not already present
|
|
171
264
|
const flag = info.flag
|
|
172
265
|
const escapedCmd = cmd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+')
|
|
173
266
|
const escapedFlag = flag.replace(/-/g, '\\-')
|
|
@@ -203,16 +296,12 @@ async function patchPackageScripts(projectRoot, configsDir, logger) {
|
|
|
203
296
|
}
|
|
204
297
|
}
|
|
205
298
|
|
|
206
|
-
if (changed)
|
|
207
|
-
await writeJsonFile(pkgPath, pkg)
|
|
208
|
-
}
|
|
299
|
+
if (changed) await writeJsonFile(pkgPath, pkg)
|
|
209
300
|
return changed
|
|
210
301
|
}
|
|
211
302
|
|
|
212
303
|
/**
|
|
213
304
|
* Add "prepare": "rootless prepare --yes" to project's package.json.
|
|
214
|
-
* If prepare already exists and doesn't include rootless, prepends to it.
|
|
215
|
-
* Returns the final prepare script value.
|
|
216
305
|
*/
|
|
217
306
|
async function addPrepareHook(projectRoot) {
|
|
218
307
|
const pkgPath = path.join(projectRoot, 'package.json')
|
|
@@ -232,4 +321,14 @@ async function addPrepareHook(projectRoot) {
|
|
|
232
321
|
return pkg.scripts.prepare
|
|
233
322
|
}
|
|
234
323
|
|
|
235
|
-
export {
|
|
324
|
+
export {
|
|
325
|
+
patchPackageScripts,
|
|
326
|
+
addPrepareHook,
|
|
327
|
+
buildConfigMap,
|
|
328
|
+
patchScriptString,
|
|
329
|
+
isPatchable,
|
|
330
|
+
isRootRequired,
|
|
331
|
+
getAllKnownFiles,
|
|
332
|
+
NEVER_MIGRATE,
|
|
333
|
+
}
|
|
334
|
+
|