Skip to content
This repository was archived by the owner on Apr 1, 2023. It is now read-only.

Commit 3ad01c0

Browse files
Merge pull request #193 from CarterMcAlister/v2-eject
V2 - Implement eject script
2 parents 39db65b + a157324 commit 3ad01c0

File tree

1 file changed

+225
-8
lines changed
  • packages/crl-scripts/lib/commands

1 file changed

+225
-8
lines changed
Lines changed: 225 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,232 @@
11
'use strict'
2+
const chalk = require('chalk')
3+
const inquirer = require('inquirer')
4+
const path = require('path')
5+
const spawnSync = require('react-dev-utils/crossSpawn').sync
6+
const execSync = require('child_process').execSync
7+
const os = require('os')
8+
const fs = require('fs-extra')
29

3-
module.exports = (program) => {
10+
const green = chalk.green
11+
const cyan = chalk.cyan
12+
const red = chalk.red
13+
14+
module.exports = program => {
415
program
516
.command('eject')
617
.description('Ejects your library from crl-scripts')
7-
.action(async () => {
8-
try {
9-
console.error('Error this functionality has yet to be implemented.')
10-
process.exit(1)
11-
} catch (err) {
12-
program.handleError(err)
13-
}
18+
.action(() => {
19+
const appDirectory = fs.realpathSync(process.cwd())
20+
const getAppPath = relativePath =>
21+
path.resolve(appDirectory, relativePath)
22+
const getOwnPath = relativePath =>
23+
path.resolve(__dirname, '..', relativePath)
24+
25+
inquirer
26+
.prompt({
27+
type: 'confirm',
28+
name: 'shouldEject',
29+
message: 'Are you sure you want to eject? This action is permanent.',
30+
default: false
31+
})
32+
.then(answer => {
33+
if (!answer.shouldEject) {
34+
console.log(cyan('Close one! Eject aborted.'))
35+
return
36+
}
37+
console.log('Eject Starting')
38+
39+
const gitStatus = getGitStatus()
40+
if (gitStatus) {
41+
console.error(
42+
red(
43+
'This git repository has untracked files or uncommitted changes:'
44+
) +
45+
'\n\n' +
46+
gitStatus
47+
.split('\n')
48+
.map(line => line.match(/ .*/g)[0].trim())
49+
.join('\n') +
50+
'\n\n' +
51+
red(
52+
'Remove untracked files, stash or commit any changes, and try again.'
53+
)
54+
)
55+
process.exit(1)
56+
}
57+
58+
const ownPath = getOwnPath('../')
59+
const appPath = getAppPath('.')
60+
61+
function verifyAbsent(file) {
62+
if (fs.existsSync(path.join(appPath, file))) {
63+
console.error(
64+
`\`${file}\` already exists in your app folder. We cannot ` +
65+
'continue as you would lose all the changes in that file or directory. ' +
66+
'Please move or delete it (maybe make a copy for backup) and run this ' +
67+
'command again.'
68+
)
69+
process.exit(1)
70+
}
71+
}
72+
73+
const folders = ['lib', 'lib/commands']
74+
75+
// Make shallow array of files paths
76+
const files = folders.reduce((files, folder) => {
77+
return files.concat(
78+
fs
79+
.readdirSync(path.join(ownPath, folder))
80+
// set full path
81+
.map(file => path.join(ownPath, folder, file))
82+
// omit dirs from file list
83+
.filter(file => fs.lstatSync(file).isFile())
84+
)
85+
}, [])
86+
87+
folders.forEach(verifyAbsent)
88+
files.forEach(verifyAbsent)
89+
90+
console.log()
91+
console.log(cyan(`Copying files into ${appPath}`))
92+
93+
folders.forEach(folder => {
94+
fs.mkdirSync(path.join(appPath, folder))
95+
})
96+
97+
console.log()
98+
99+
files.forEach(file => {
100+
const content = fs.readFileSync(file, 'utf8')
101+
console.log(
102+
` Adding ${cyan(file.replace(ownPath, ''))} to the project`
103+
)
104+
fs.writeFileSync(file.replace(ownPath, appPath), content)
105+
})
106+
console.log()
107+
108+
const ownPackage = require(path.join(ownPath, 'package.json'))
109+
const appPackage = require(path.join(appPath, 'package.json'))
110+
111+
console.log(cyan('Updating the dependencies'))
112+
const ownPackageName = ownPackage.name
113+
114+
if (appPackage.devDependencies) {
115+
if (appPackage.devDependencies[ownPackageName]) {
116+
console.log(
117+
` Removing ${cyan(ownPackageName)} from devDependencies`
118+
)
119+
delete appPackage.devDependencies[ownPackageName]
120+
}
121+
}
122+
Object.keys(ownPackage.dependencies).forEach(key => {
123+
if (
124+
ownPackage.optionalDependencies &&
125+
ownPackage.optionalDependencies[key]
126+
) {
127+
return
128+
}
129+
console.log(` Adding ${cyan(key)} to dependencies`)
130+
appPackage.dependencies[key] = ownPackage.dependencies[key]
131+
})
132+
// Sort the deps
133+
const unsortedDependencies = appPackage.dependencies
134+
appPackage.dependencies = {}
135+
Object.keys(unsortedDependencies)
136+
.sort()
137+
.forEach(key => {
138+
appPackage.dependencies[key] = unsortedDependencies[key]
139+
})
140+
console.log()
141+
142+
console.log(cyan('Updating the scripts'))
143+
delete appPackage.scripts['eject']
144+
Object.keys(appPackage.scripts).forEach(key => {
145+
Object.keys(ownPackage.bin).forEach(binKey => {
146+
const regex = new RegExp(binKey + ' (\\w+)', 'g')
147+
if (!regex.test(appPackage.scripts[key])) {
148+
return
149+
}
150+
appPackage.scripts[key] = appPackage.scripts[key].replace(
151+
regex,
152+
'node lib/cli.js $1'
153+
)
154+
console.log(
155+
` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan(
156+
`"node lib/cli.js ${key}"`
157+
)}`
158+
)
159+
})
160+
})
161+
162+
console.log()
163+
console.log(cyan('Configuring package.json'))
164+
165+
fs.writeFileSync(
166+
path.join(appPath, 'package.json'),
167+
JSON.stringify(appPackage, null, 2) + os.EOL
168+
)
169+
console.log()
170+
171+
// "Don't destroy what isn't ours"
172+
if (ownPath.indexOf(appPath) === 0) {
173+
try {
174+
// remove crl-scripts and crl-scripts binaries from app node_modules
175+
Object.keys(ownPackage.bin).forEach(binKey => {
176+
console.log(path.join(appPath, 'node_modules', '.bin', binKey))
177+
fs.removeSync(
178+
path.join(appPath, 'node_modules', '.bin', binKey)
179+
)
180+
})
181+
fs.removeSync(ownPath)
182+
} catch (e) {
183+
// It's not essential that this succeeds
184+
}
185+
}
186+
187+
if (fs.existsSync(getAppPath('yarn.lock'))) {
188+
console.log(cyan('Running yarn...'))
189+
spawnSync('yarnpkg', ['--cwd', process.cwd()], {
190+
stdio: 'inherit'
191+
})
192+
} else {
193+
console.log(cyan('Running npm install...'))
194+
spawnSync('npm', ['install', '--loglevel', 'error'], {
195+
stdio: 'inherit'
196+
})
197+
}
198+
199+
console.log(green('Ejected successfully!'))
200+
console.log()
201+
202+
if (tryGitAdd(appPath)) {
203+
console.log(cyan('Staged ejected files for commit.'))
204+
console.log()
205+
}
206+
console.log()
207+
})
14208
})
15209
}
210+
211+
function getGitStatus() {
212+
try {
213+
const stdout = execSync(`git status --porcelain`, {
214+
stdio: ['pipe', 'pipe', 'ignore']
215+
}).toString()
216+
return stdout.trim()
217+
} catch (e) {
218+
return ''
219+
}
220+
}
221+
222+
function tryGitAdd(appPath) {
223+
try {
224+
spawnSync('git', ['add', path.join(appPath, 'lib')], {
225+
stdio: 'inherit'
226+
})
227+
228+
return true
229+
} catch (e) {
230+
return false
231+
}
232+
}

0 commit comments

Comments
 (0)