-
-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add saber eject-theme command (#259)
* feat: add saber eject * docs: elaborate eject command * deps: replace sharp with jimp * feat: add dependency merging * upgrade from master * fix: linting * fix: full compliance with package.json repository * tweaks: use path.resolve * tweak the logic for installing dependencies * use npm if package-lock.json is present * rename saber eject to saber eject-theme
- Loading branch information
Showing
10 changed files
with
800 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const { setNodeEnv, handleError } = require('./utils') | ||
|
||
module.exports = function(cli) { | ||
cli | ||
.command( | ||
'build [app-path]', | ||
'Compile the application and generate static HTML files' | ||
) | ||
.alias('generate') | ||
.option('--skip-compilation', 'Skip the webpack compilation process') | ||
.option('--inspect-webpack', 'Inspect webpack config in your editor') | ||
.action((cwd = '.', options) => { | ||
setNodeEnv('production') | ||
|
||
if (cli.matchedCommandName === 'generate') { | ||
require('saber-log').log.warn( | ||
`The "generate" command is now deprecated, please use "build" instead.` | ||
) | ||
} | ||
|
||
const { skipCompilation } = options | ||
delete options.skipCompilation | ||
return require('..')(Object.assign({ cwd, dev: false }, options)) | ||
.build({ skipCompilation }) | ||
.catch(handleError) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
const { setNodeEnv, handleError } = require('./utils') | ||
|
||
module.exports = function(cli) { | ||
cli | ||
.command('[app-path]', 'Run the application in dev mode', { | ||
ignoreOptionDefaultValue: true | ||
}) | ||
.alias('dev') | ||
.option('--lazy', 'Enable lazy page compilation') | ||
.option('--port <port>', 'Server port', { default: 3000 }) | ||
.option('--host <host>', 'Server host', { default: '0.0.0.0' }) | ||
.option('--ssr', 'Enable server-side rendering') | ||
.option('--inspect-webpack', 'Inspect webpack config in your editor') | ||
.action((cwd = '.', options) => { | ||
setNodeEnv('development') | ||
|
||
const { host, port, lazy, ssr } = options | ||
delete options.host | ||
delete options.port | ||
delete options.lazy | ||
delete options.ssr | ||
return require('..')(Object.assign({ cwd, dev: true }, options), { | ||
server: { | ||
host, | ||
port, | ||
ssr | ||
}, | ||
build: { | ||
lazy | ||
} | ||
}) | ||
.serve() | ||
.catch(handleError) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
const path = require('path') | ||
const { spawnSync } = require('child_process') | ||
const { fs } = require('saber-utils') | ||
const { log } = require('saber-log') | ||
const normalizeRepo = require('normalize-repo') | ||
const downloadGitRepo = require('download-git-repo') | ||
const configLoader = require('../utils/configLoader') | ||
const resolvePackage = require('../utils/resolvePackage') | ||
const { handleError, spawn } = require('./utils') | ||
|
||
const downloadRepo = (url, dest, opts) => | ||
new Promise(resolve => downloadGitRepo(url, dest, opts, resolve)) | ||
|
||
module.exports = function(cli) { | ||
cli | ||
.command( | ||
'eject-theme [app-path]', | ||
`Copy the currently used theme's source code to a local folder.` | ||
) | ||
.option( | ||
'--git', | ||
'Pull code from Git, instead of node_modules, and add the theme as a submodule', | ||
{ default: false } | ||
) | ||
.option( | ||
'--merge-dependencies', | ||
"Copy over the theme's dependencies to your project's package.json.", | ||
{ default: true } | ||
) | ||
.option('--path <path>', 'Ejected theme destination', { | ||
default: './theme' | ||
}) | ||
.action(async (cwd = '.', options) => { | ||
cwd = path.resolve(cwd) | ||
const { git } = options | ||
const mergeDependencies = options['merge-dependencies'] | ||
|
||
const config = | ||
configLoader.load({ cwd, files: configLoader.CONFIG_FILES }).data || {} | ||
if (!config.theme) { | ||
handleError('No theme specified in config.') | ||
} | ||
|
||
const destPath = path.join(cwd, options.path) | ||
const relativeDest = path.relative(cwd, destPath) | ||
if (await fs.pathExists(destPath)) { | ||
handleError( | ||
`The path ${options.path} already exists. Please specify a different one using "--path".` | ||
) | ||
} | ||
|
||
const themePath = resolvePackage(config.theme, { | ||
prefix: 'saber-theme-', | ||
cwd | ||
}) | ||
if (!themePath) { | ||
handleError( | ||
`Theme "${config.theme}" could not be found in your node_modules.` | ||
) | ||
} | ||
|
||
const themePackage = configLoader.load({ | ||
cwd: themePath, | ||
files: ['package.json'] | ||
}).data | ||
|
||
if (git) { | ||
const repo = themePackage.repository | ||
|
||
if (repo && (!repo.type || repo.type === 'git')) { | ||
const tmp = path.join(cwd, '.saber', 'theme-tmp') | ||
|
||
const { shortcut, url } = normalizeRepo(themePackage.repository) | ||
const downloadError = await downloadRepo(shortcut || url, tmp, { | ||
clone: Boolean(shortcut) | ||
}) | ||
|
||
if (downloadError) { | ||
handleError(downloadError) | ||
} | ||
|
||
try { | ||
await fs.move( | ||
repo.directory ? path.join(tmp, repo.directory) : tmp, | ||
destPath | ||
) | ||
|
||
await fs.remove(tmp) | ||
} catch (error) { | ||
handleError(error) | ||
} | ||
|
||
log.success('Downloaded theme source via Git.') | ||
} else { | ||
handleError( | ||
'The theme has no git repository specified within its package.json.' | ||
) | ||
} | ||
} else { | ||
try { | ||
await fs.copy(themePath, destPath, { | ||
filter: src => !src.endsWith('/node_modules') | ||
}) | ||
|
||
log.info('Copied theme from node_modules.') | ||
} catch (error) { | ||
handleError(error) | ||
} | ||
} | ||
|
||
if (mergeDependencies) { | ||
const dependencies = themePackage.dependencies || {} | ||
const devDependencies = themePackage.devDependencies || {} | ||
|
||
const projectPackage = configLoader.load({ | ||
cwd, | ||
files: ['package.json'] | ||
}).data | ||
|
||
try { | ||
await fs.writeJson( | ||
path.join(cwd, 'package.json'), | ||
{ | ||
...projectPackage, | ||
dependencies: { | ||
...projectPackage.dependencies, | ||
...dependencies, | ||
[themePackage.name]: undefined // remove theme from dependencies | ||
}, | ||
devDependencies: { | ||
...projectPackage.devDependencies, | ||
...devDependencies, | ||
[themePackage.name]: undefined // remove theme from dev dependencies | ||
} | ||
}, | ||
{ spaces: 2 } | ||
) | ||
|
||
const { status } = spawnSync('yarnpkg', ['--version']) // test if yarn is present before allowing it to use the same stdio | ||
const hasNpmLock = await fs.pathExists( | ||
path.join(cwd, 'package-lock.json') | ||
) | ||
if (status === 0 && !hasNpmLock) { | ||
await spawn('yarn', ['install'], { stdio: 'inherit' }) | ||
} else { | ||
await spawn('npm', ['install'], { stdio: 'inherit' }) | ||
} | ||
|
||
log.success('Merged theme dependencies.') | ||
} catch (error) { | ||
handleError(error) | ||
} | ||
} | ||
|
||
log.info( | ||
`Please change "theme" in your Saber config to "./${relativeDest}".` | ||
) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const commands = ['dev', 'build', 'serve', 'eject-theme'] | ||
|
||
module.exports = function(cli) { | ||
commands.forEach(command => require(`./${command}`)(cli)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const { setNodeEnv, handleError } = require('./utils') | ||
|
||
module.exports = function(cli) { | ||
cli | ||
.command('serve [app-path]', 'Serve the output directory') | ||
.option('--host <host>', 'Server host', { default: '0.0.0.0' }) | ||
.option('--port <port>', 'Server port', { default: 3000 }) | ||
.action((cwd = '.', options) => { | ||
setNodeEnv('production') | ||
|
||
const { host, port } = options | ||
delete options.host | ||
delete options.port | ||
return require('..')(Object.assign({ cwd, dev: true }, options), { | ||
server: { | ||
host, | ||
port | ||
} | ||
}) | ||
.serveOutDir() | ||
.catch(handleError) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const { spawn } = require('child_process') | ||
|
||
module.exports = { | ||
setNodeEnv(env) { | ||
if (!process.env.NODE_ENV) { | ||
process.env.NODE_ENV = env | ||
} | ||
}, | ||
handleError(err) { | ||
if (typeof err === 'string') err = new Error(err) | ||
require('saber-log').log.error(err.stack) | ||
process.exit(1) // eslint-disable-line | ||
}, | ||
spawn(...args) { | ||
return new Promise((resolve, reject) => { | ||
const childProcess = spawn(...args) | ||
childProcess.on('close', resolve) | ||
childProcess.on('error', reject) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.