diff --git a/packages/cli/src/commands/app/diff.ts b/packages/cli/src/commands/app/diff.ts new file mode 100644 index 0000000000..a667407f23 --- /dev/null +++ b/packages/cli/src/commands/app/diff.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Command } from 'commander'; +import { + diffTemplateFiles, + handlers, + handleAllFiles, + inquirerPromptFunc, + makeCheckPromptFunc, + yesPromptFunc, +} from '../../lib/diff'; +import { version } from '../../lib/version'; + +const fileHandlers = [ + { + patterns: ['packages/app/package.json'], + handler: handlers.appPackageJson, + }, + { + patterns: [/tsconfig\.json$/], + handler: handlers.exactMatch, + }, + { + patterns: [ + /README\.md$/, + /\.eslintrc\.js$/, + // make sure files in 1st level of src/ and dev/ exist + /^packages\/app\/(src|dev)\/[^/]+$/, + ], + handler: handlers.exists, + }, + { + patterns: [ + 'lerna.json', + /^src\//, + /^patches\//, + /^packages\/app\/public\//, + /^packages\/app\/cypress/, + // Let plugin:diff take care of the plugins + /^plugins/, + /package\.json$/, + ], + handler: handlers.skip, + }, +]; + +export default async (cmd: Command) => { + let promptFunc = inquirerPromptFunc; + let finalize = () => {}; + + if (cmd.check) { + [promptFunc, finalize] = makeCheckPromptFunc(); + } else if (cmd.yes) { + promptFunc = yesPromptFunc; + } + + const templateFiles = await diffTemplateFiles('default-app', { version }); + await handleAllFiles(fileHandlers, templateFiles, promptFunc); + await finalize(); +}; diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index f02bf6524f..bdd0d3e1f2 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -39,6 +39,13 @@ const main = (argv: string[]) => { .option('--check', 'Enable type checking and linting') .action(actionHandler(() => require('./commands/app/serve'))); + program + .command('app:diff') + .option('--check', 'Fail if changes are required') + .option('--yes', 'Apply all changes') + .description('Diff an existing app with the creation template') + .action(actionHandler(() => require('./commands/app/diff'))); + program .command('create-plugin') .description('Creates a new plugin in the current repository') @@ -157,7 +164,7 @@ function actionHandler( }; } -process.on('unhandledRejection', (rejection) => { +process.on('unhandledRejection', rejection => { if (rejection instanceof Error) { exitWithError(rejection); } else { diff --git a/packages/cli/src/lib/diff/handlers.ts b/packages/cli/src/lib/diff/handlers.ts index 9e46dd92cf..c4efacb198 100644 --- a/packages/cli/src/lib/diff/handlers.ts +++ b/packages/cli/src/lib/diff/handlers.ts @@ -22,6 +22,7 @@ class PackageJsonHandler { static async handler( { path, write, missing, targetContents, templateContents }: FileDiff, prompt: PromptFunc, + variant?: string, ) { console.log('Checking package.json'); @@ -32,19 +33,33 @@ class PackageJsonHandler { const pkg = JSON.parse(templateContents); const targetPkg = JSON.parse(targetContents); - const handler = new PackageJsonHandler(write, prompt, pkg, targetPkg); + const handler = new PackageJsonHandler( + write, + prompt, + pkg, + targetPkg, + variant, + ); await handler.handle(); } + static async appHandler(file: FileDiff, prompt: PromptFunc) { + return PackageJsonHandler.handler(file, prompt, 'app'); + } + constructor( private readonly writeFunc: WriteFileFunc, private readonly prompt: PromptFunc, private readonly pkg: any, private readonly targetPkg: any, + private readonly variant?: string, ) {} async handle() { await this.syncField('main'); + if (this.variant !== 'app') { + await this.syncField('main:src'); + } await this.syncField('types'); await this.syncField('files'); await this.syncScripts(); @@ -78,7 +93,7 @@ class PackageJsonHandler { targetObj[fieldName] = newValue; await this.write(); } - } else { + } else if (fieldName in obj) { if ( await this.prompt( `package.json is missing field ${fullFieldName}, set to ${coloredNewValue}?`, @@ -95,6 +110,10 @@ class PackageJsonHandler { const targetScripts = (this.targetPkg.scripts = this.targetPkg.scripts || {}); + if (!pkgScripts) { + return; + } + for (const key of Object.keys(pkgScripts)) { await this.syncField(key, pkgScripts, targetScripts, 'scripts'); } @@ -132,7 +151,14 @@ class PackageJsonHandler { const targetDeps = (this.targetPkg[fieldName] = this.targetPkg[fieldName] || {}); + if (!pkgDeps) { + return; + } + for (const key of Object.keys(pkgDeps)) { + if (this.variant === 'app' && key.startsWith('plugin-')) { + continue; + } await this.syncField(key, pkgDeps, targetDeps, fieldName); } } @@ -206,6 +232,7 @@ export const handlers = { exists: existsHandler, exactMatch: exactMatchHandler, packageJson: PackageJsonHandler.handler, + appPackageJson: PackageJsonHandler.appHandler, }; export async function handleAllFiles( diff --git a/packages/cli/templates/default-app/packages/app/tsconfig.json b/packages/cli/templates/default-app/packages/app/tsconfig.json deleted file mode 100644 index 0457810645..0000000000 --- a/packages/cli/templates/default-app/packages/app/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "@spotify/web-scripts/config/tsconfig.json", - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react", - "incremental": false - }, - "include": ["src"] -} diff --git a/packages/cli/templates/default-app/plugins/welcome/tsconfig.json b/packages/cli/templates/default-app/plugins/welcome/tsconfig.json deleted file mode 100644 index 5a3931ffce..0000000000 --- a/packages/cli/templates/default-app/plugins/welcome/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["src"], - "compilerOptions": {} -} diff --git a/packages/cli/templates/default-app/prettier.config.js b/packages/cli/templates/default-app/prettier.config.js deleted file mode 100644 index 93df970dd6..0000000000 --- a/packages/cli/templates/default-app/prettier.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@spotify/web-scripts/config/prettier.config.js');