diff --git a/docusaurus/docs/custom-templates.md b/docusaurus/docs/custom-templates.md index 98acb9b1a75..bab1c54a00d 100644 --- a/docusaurus/docs/custom-templates.md +++ b/docusaurus/docs/custom-templates.md @@ -58,18 +58,31 @@ You can add whatever files you want in here, but you must have at least the file ### The `template.json` file -This is where you can define dependencies (only dependencies are supported for now), as well as any custom scripts that your template relies on. +This is the configuration file for your template. As this is a new feature, more options will be added over time. For now, only a `package` key is supported. + +The `package` key lets you provide any keys/values that you want added to the new project's `package.json`, such as dependencies (only dependencies are supported for now) and any custom scripts that your template relies on. + +Below is an example `template.json` file: ```json { - "dependencies": { - "serve": "^11.2.0" - }, - "scripts": { - "serve": "serve -s build", - "build-and-serve": "npm run build && npm run serve" + "package": { + "dependencies": { + "eslint-plugin-jsx-a11y": "^6.2.3", + "serve": "^11.2.0" + }, + "scripts": { + "serve": "serve -s build", + "build-and-serve": "npm run build && npm run serve" + }, + "eslintConfig": { + "extends": ["react-app", "plugin:jsx-a11y/recommended"], + "plugins": ["jsx-a11y"] + } } } ``` +Any values you add for `"dependencies"` and `"scripts"` will be merged with the Create React App defaults. Values for any other keys will be used as-is, replacing any matching Create React App defaults. + For convenience, we always replace `npm run` with `yarn` in your custom `"scripts"`, as well as in your `README` when projects are initialized with yarn. diff --git a/packages/cra-template-typescript/template.json b/packages/cra-template-typescript/template.json index 06d73b74130..1bb882699f6 100644 --- a/packages/cra-template-typescript/template.json +++ b/packages/cra-template-typescript/template.json @@ -1,12 +1,14 @@ { - "dependencies": { - "@testing-library/react": "^9.3.2", - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/user-event": "^7.1.2", - "@types/node": "^12.0.0", - "@types/react": "^16.9.0", - "@types/react-dom": "^16.9.0", - "@types/jest": "^24.0.0", - "typescript": "~3.7.2" + "package": { + "dependencies": { + "@testing-library/react": "^9.3.2", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/user-event": "^7.1.2", + "@types/node": "^12.0.0", + "@types/react": "^16.9.0", + "@types/react-dom": "^16.9.0", + "@types/jest": "^24.0.0", + "typescript": "~3.7.2" + } } } diff --git a/packages/cra-template/template.json b/packages/cra-template/template.json index 0cc6ba23920..4c6861dc1f0 100644 --- a/packages/cra-template/template.json +++ b/packages/cra-template/template.json @@ -1,7 +1,9 @@ { - "dependencies": { - "@testing-library/react": "^9.3.2", - "@testing-library/jest-dom": "^4.2.4", - "@testing-library/user-event": "^7.1.2" + "package": { + "dependencies": { + "@testing-library/react": "^9.3.2", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/user-event": "^7.1.2" + } } } diff --git a/packages/react-scripts/fixtures/kitchensink/template.json b/packages/react-scripts/fixtures/kitchensink/template.json index 3ba93127c8b..954cb3b2d73 100644 --- a/packages/react-scripts/fixtures/kitchensink/template.json +++ b/packages/react-scripts/fixtures/kitchensink/template.json @@ -1,10 +1,12 @@ { - "dependencies": { - "bootstrap": "4.3.1", - "jest": "24.9.0", - "node-sass": "4.12.0", - "normalize.css": "7.0.0", - "prop-types": "15.7.2", - "test-integrity": "2.0.1" + "package": { + "dependencies": { + "bootstrap": "4.3.1", + "jest": "24.9.0", + "node-sass": "4.12.0", + "normalize.css": "7.0.0", + "prop-types": "15.7.2", + "test-integrity": "2.0.1" + } } } diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index 74d607b105d..9c78fcfbfb5 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -118,11 +118,55 @@ module.exports = function( templateJson = require(templateJsonPath); } + const templatePackage = templateJson.package || {}; + + // Keys to ignore in templatePackage + const templatePackageBlacklist = [ + 'name', + 'version', + 'description', + 'keywords', + 'bugs', + 'license', + 'author', + 'contributors', + 'files', + 'main', + 'browser', + 'bin', + 'man', + 'directories', + 'repository', + 'devDependencies', + 'peerDependencies', + 'bundledDependencies', + 'optionalDependencies', + 'engineStrict', + 'os', + 'cpu', + 'preferGlobal', + 'private', + 'publishConfig', + ]; + + // Keys from templatePackage that will be merged with appPackage + const templatePackageToMerge = ['dependencies', 'scripts']; + + // Keys from templatePackage that will be added to appPackage, + // replacing any existing entries. + const templatePackageToReplace = Object.keys(templatePackage).filter(key => { + return ( + !templatePackageBlacklist.includes(key) && + !templatePackageToMerge.includes(key) + ); + }); + // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {}; // Setup the script rules - const templateScripts = templateJson.scripts || {}; + // TODO: deprecate 'scripts' key directly on templateJson + const templateScripts = templatePackage.scripts || templateJson.scripts || {}; appPackage.scripts = Object.assign( { start: 'react-scripts start', @@ -152,6 +196,11 @@ module.exports = function( // Setup the browsers list appPackage.browserslist = defaultBrowsers; + // Add templatePackage keys/values to appPackage, replacing existing entries + templatePackageToReplace.forEach(key => { + appPackage[key] = templatePackage[key]; + }); + fs.writeFileSync( path.join(appPath, 'package.json'), JSON.stringify(appPackage, null, 2) + os.EOL @@ -221,7 +270,9 @@ module.exports = function( } // Install additional template dependencies, if present - const templateDependencies = templateJson.dependencies; + // TODO: deprecate 'dependencies' key directly on templateJson + const templateDependencies = + templatePackage.dependencies || templateJson.dependencies; if (templateDependencies) { args = args.concat( Object.keys(templateDependencies).map(key => {