From a851637bd8a8c423e959eb014a5b2823d7b4790b Mon Sep 17 00:00:00 2001 From: Mike Grabowski Date: Mon, 6 May 2019 13:59:36 +0200 Subject: [PATCH 1/5] docs: add `maintainers` section (#369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `maintainers` section * Update README.md * Polacy nie gęsi, swój język mają xd --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 47ea151eb..aa36c7094 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,12 @@ You can also add npm scripts to call it with whichever package manager you use: } ``` +## Maintainers + +- Michał Pierzchała ([**@thymikee**](https://github.com/thymikee)) - [Callstack](https://callstack.com) +- Mike Grabowski ([**@grabbou**](https://github.com/grabbou)) - [Callstack](https://callstack.com) +- Kacper Wiszczuk ([**@esemesek**](https://github.com/esemesek)) - [Callstack](https://callstack.com) + ## License Everything inside this repository is [MIT licensed](./LICENSE). From 8ba322ebbaccd2d0ae7dece5859c1d1f99d49dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 6 May 2019 16:30:05 +0200 Subject: [PATCH 2/5] fix: refactor init for less duplication; fix handling versioned templates (#364) * chore: refactor init for less duplication * fix handling versioned packages * polish error messages a bit * support npm tags --- .../init/__tests__/templateName.test.js | 45 +++---- packages/cli/src/commands/init/init.js | 112 ++++++------------ .../cli/src/commands/init/templateName.js | 17 +++ 3 files changed, 75 insertions(+), 99 deletions(-) diff --git a/packages/cli/src/commands/init/__tests__/templateName.test.js b/packages/cli/src/commands/init/__tests__/templateName.test.js index 358bd76bd..db972581b 100644 --- a/packages/cli/src/commands/init/__tests__/templateName.test.js +++ b/packages/cli/src/commands/init/__tests__/templateName.test.js @@ -4,13 +4,10 @@ import {fetch} from '../../../tools/fetch'; jest.mock('../../../tools/fetch', () => ({fetch: jest.fn()})); -const VERSION = '0.58.0'; -const RN_WITH_VERSION = 'react-native@0.58.0'; +const RN_NPM_PACKAGE = 'react-native'; const ABS_RN_PATH = '/path/to/react-native'; -const ABS_RN_TARBALL_PATH = '/path/to/react-native/react-native-1.2.3-rc.0.tgz'; -const PACKAGE_NAME = 'react-native'; -test('should support file protocol with absolute path', async () => { +test('supports file protocol with absolute path', async () => { jest.mock( `${ABS_RN_PATH}/package.json`, () => ({ @@ -20,18 +17,11 @@ test('should support file protocol with absolute path', async () => { ); expect(await processTemplateName(`file://${ABS_RN_PATH}`)).toEqual({ uri: ABS_RN_PATH, - name: PACKAGE_NAME, + name: RN_NPM_PACKAGE, }); }); -test('should get default package if none protocols were handled', async () => { - expect(await processTemplateName(VERSION)).toEqual({ - uri: VERSION, - name: VERSION, - }); -}); - -test('should support shorthand templates', async () => { +test('supports shorthand templates', async () => { const templateName = 'typescript'; (fetch: any).mockImplementationOnce(() => { return Promise.resolve(`{"name": "react-native-template-${templateName}"}`); @@ -42,7 +32,7 @@ test('should support shorthand templates', async () => { }); }); -test('should support not-found shorthand templates', async () => { +test('supports not-found shorthand templates', async () => { const templateName = 'typescriptz'; (fetch: any).mockImplementationOnce(() => { return Promise.resolve('Not found'); @@ -53,14 +43,29 @@ test('should support not-found shorthand templates', async () => { }); }); -test('should get package if none protocols were handled', async () => { - expect(await processTemplateName(RN_WITH_VERSION)).toEqual({ - uri: RN_WITH_VERSION, - name: RN_WITH_VERSION, +test('supports npm packages as template names', async () => { + expect(await processTemplateName(RN_NPM_PACKAGE)).toEqual({ + uri: RN_NPM_PACKAGE, + name: RN_NPM_PACKAGE, }); }); -test('should support path to tgz archives', async () => { +test.each` + templateName | uri | name + ${'react-native@0.58.0'} | ${'react-native@0.58.0'} | ${'react-native'} + ${'some-name@latest'} | ${'some-name@latest'} | ${'some-name'} + ${'@scoped/name@0.58.0'} | ${'@scoped/name@0.58.0'} | ${'@scoped/name'} + ${'@scoped/name@tag'} | ${'@scoped/name@tag'} | ${'@scoped/name'} +`( + 'supports versioned npm package "$templateName" as template name', + async ({templateName, uri, name}) => { + expect(await processTemplateName(templateName)).toEqual({uri, name}); + }, +); + +test('supports path to tgz archives', async () => { + const ABS_RN_TARBALL_PATH = + '/path/to/react-native/react-native-1.2.3-rc.0.tgz'; expect(await processTemplateName(`file://${ABS_RN_TARBALL_PATH}`)).toEqual({ uri: `file://${ABS_RN_TARBALL_PATH}`, name: 'react-native', diff --git a/packages/cli/src/commands/init/init.js b/packages/cli/src/commands/init/init.js index c3b306b71..c88d4fddf 100644 --- a/packages/cli/src/commands/init/init.js +++ b/packages/cli/src/commands/init/init.js @@ -39,93 +39,45 @@ function adjustNameIfUrl(name, cwd) { return name; } -async function createFromExternalTemplate( +async function createFromTemplate({ + projectName, + templateName, + version, + npm, +}: { projectName: string, templateName: string, + version?: string, npm?: boolean, -) { - logger.debug('Initializing new project from external template'); +}) { + logger.debug('Initializing new project'); logger.log(banner); - const Loader = getLoader(); - const loader = new Loader({text: 'Downloading template'}); - loader.start(); - const templateSourceDir = fs.mkdtempSync( path.join(os.tmpdir(), 'rncli-init-template-'), ); - try { - let {uri, name} = await processTemplateName(templateName); - await installTemplatePackage(uri, templateSourceDir, npm); - loader.succeed(); - loader.start('Copying template'); - - name = adjustNameIfUrl(name, templateSourceDir); - const templateConfig = getTemplateConfig(name, templateSourceDir); - await copyTemplate(name, templateConfig.templateDir, templateSourceDir); - - loader.succeed(); - loader.start('Preparing template'); - changePlaceholderInTemplate(projectName, templateConfig.placeholderName); - - loader.succeed(); - const {postInitScript} = templateConfig; - if (postInitScript) { - // Leaving trailing space because there may be stdout from the script - loader.start('Executing post init script '); - await executePostInitScript(name, postInitScript, templateSourceDir); - loader.succeed(); - } - - loader.start('Installing all required dependencies'); - await PackageManager.installAll({preferYarn: !npm, silent: true}); - loader.succeed(); - } catch (e) { - loader.fail(); - throw new Error(e); - } finally { - fs.removeSync(templateSourceDir); + if (version && semver.valid(version) && !semver.gte(version, '0.60.0-rc.0')) { + throw new Error( + 'Cannot use React Native CLI to initialize project with version lower than 0.60.0.', + ); } -} -async function createFromReactNativeTemplate( - projectName: string, - version: string, - npm?: boolean, -) { - logger.debug('Initializing new project'); - logger.log(banner); - - const Loader = getLoader(); - const loader = new Loader({text: 'Downloading template'}); - loader.start(); - const templateSourceDir = fs.mkdtempSync( - path.join(os.tmpdir(), 'rncli-init-template-'), - ); try { - if (semver.valid(version) && !semver.gte(version, '0.60.0')) { - throw new Error( - 'Cannot use React Native CLI to initialize project with version lower than 0.60.0', - ); - } - - const TEMPLATE_NAME = 'react-native'; - - const {uri} = await processTemplateName(`${TEMPLATE_NAME}@${version}`); + loader.start(); + let {uri, name} = await processTemplateName( + version ? `${templateName}@${version}` : templateName, + ); await installTemplatePackage(uri, templateSourceDir, npm); loader.succeed(); loader.start('Copying template'); - const templateConfig = getTemplateConfig(TEMPLATE_NAME, templateSourceDir); - await copyTemplate( - TEMPLATE_NAME, - templateConfig.templateDir, - templateSourceDir, - ); + name = adjustNameIfUrl(name, templateSourceDir); + const templateConfig = getTemplateConfig(name, templateSourceDir); + await copyTemplate(name, templateConfig.templateDir, templateSourceDir); loader.succeed(); loader.start('Processing template'); @@ -135,12 +87,9 @@ async function createFromReactNativeTemplate( loader.succeed(); const {postInitScript} = templateConfig; if (postInitScript) { - loader.start('Executing post init script'); - await executePostInitScript( - TEMPLATE_NAME, - postInitScript, - templateSourceDir, - ); + // Leaving trailing space because there may be stdout from the script + loader.start('Executing post init script '); + await executePostInitScript(name, postInitScript, templateSourceDir); loader.succeed(); } @@ -160,14 +109,19 @@ function createProject(projectName: string, options: Options, version: string) { process.chdir(projectName); if (options.template) { - return createFromExternalTemplate( + return createFromTemplate({ projectName, - options.template, - options.npm, - ); + templateName: options.template, + npm: options.npm, + }); } - return createFromReactNativeTemplate(projectName, version, options.npm); + return createFromTemplate({ + projectName, + templateName: 'react-native', + version, + npm: options.npm, + }); } export default (async function initialize( diff --git a/packages/cli/src/commands/init/templateName.js b/packages/cli/src/commands/init/templateName.js index e71351b26..d2b8b7e98 100644 --- a/packages/cli/src/commands/init/templateName.js +++ b/packages/cli/src/commands/init/templateName.js @@ -7,6 +7,7 @@ const FILE_PROTOCOL = /file:/; const HTTP_PROTOCOL = /https?:/; const TARBALL = /\.tgz$/; const VERSION_POSTFIX = /(.*)(-\d+\.\d+\.\d+)/; +const VERSIONED_PACKAGE = /(@?.*)(@)(.*)/; function handleFileProtocol(filePath: string) { const uri = new URL(filePath).pathname; @@ -32,6 +33,19 @@ function handleTarball(filePath: string) { }; } +function handleVersionedPackage(versionedPackage: string) { + const versionedPackageMatch = versionedPackage.match(VERSIONED_PACKAGE); + if (!versionedPackageMatch) { + throw new Error( + `Failed to retrieve package name. We expect the package to include name and version, e.g.: "template-name@1.2.3-rc.0", but received: "${versionedPackage}".`, + ); + } + return { + uri: versionedPackage, + name: versionedPackageMatch[1], + }; +} + export async function processTemplateName(templateName: string) { if (templateName.match(TARBALL)) { return handleTarball(templateName); @@ -39,6 +53,9 @@ export async function processTemplateName(templateName: string) { if (templateName.match(FILE_PROTOCOL)) { return handleFileProtocol(templateName); } + if (templateName.match(VERSIONED_PACKAGE)) { + return handleVersionedPackage(templateName); + } const name = await tryTemplateShorthand(templateName); From 809aef93f20b676bcf6ede7a795cb9ff62c23cd9 Mon Sep 17 00:00:00 2001 From: Ville Immonen Date: Mon, 6 May 2019 21:14:40 +0300 Subject: [PATCH 3/5] fix: reporter config option (#370) The `loadConfig` function in `metro-config` reads the `reporter` option from the second argument (`defaultConfigOverrides`) instead of the first argument (`argv`), so we need to pass it in that object to make the `customLogReporterPath` CLI option work. --- packages/cli/src/tools/loadMetroConfig.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/tools/loadMetroConfig.js b/packages/cli/src/tools/loadMetroConfig.js index da713adfc..882dccfd5 100644 --- a/packages/cli/src/tools/loadMetroConfig.js +++ b/packages/cli/src/tools/loadMetroConfig.js @@ -82,6 +82,8 @@ export type ConfigOptionsT = {| */ export default function load(ctx: ConfigT, options?: ConfigOptionsT) { const defaultConfig = getDefaultConfig(ctx); - - return loadConfig({cwd: ctx.root, ...options}, defaultConfig); + return loadConfig( + {cwd: ctx.root, ...options}, + {...defaultConfig, reporter: options && options.reporter}, + ); } From 093ce51172fc751f8c7ae81d00dc51c05f8b7c9c Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 8 May 2019 15:15:48 -0400 Subject: [PATCH 4/5] chore: Update prettier config (#379) --- .prettierrc.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.prettierrc.js b/.prettierrc.js index 50e441707..78d515160 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,5 +1,6 @@ -// added for Jest inline snapshots to not use default Prettier config module.exports = { + singleQuote: true, + trailingComma: 'all', bracketSpacing: false, - trailingComma: "all" -} + jsxBracketSameLine: true, +}; From c3ed10c0e2d5c7bfe68312093347fbf7e61aa7a7 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 8 May 2019 17:38:37 -0400 Subject: [PATCH 5/5] Use `require.resolve` to resolve package path (#378) * Use `require.resolve` to resolve package path * resolveNodeModule -> resolveNodeModuleDir --- .../config/__mocks__/resolveNodeModuleDir.js | 12 ++++++++++++ .../config/__tests__/findDependencies-test.js | 2 ++ .../src/tools/config/__tests__/index-test.js | 2 ++ packages/cli/src/tools/config/index.js | 5 +++-- .../src/tools/config/resolveNodeModuleDir.js | 19 +++++++++++++++++++ .../tools/config/resolveReactNativePath.js | 10 +++------- 6 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 packages/cli/src/tools/config/__mocks__/resolveNodeModuleDir.js create mode 100644 packages/cli/src/tools/config/resolveNodeModuleDir.js diff --git a/packages/cli/src/tools/config/__mocks__/resolveNodeModuleDir.js b/packages/cli/src/tools/config/__mocks__/resolveNodeModuleDir.js new file mode 100644 index 000000000..d85dee2ae --- /dev/null +++ b/packages/cli/src/tools/config/__mocks__/resolveNodeModuleDir.js @@ -0,0 +1,12 @@ +/** + * @flow + */ + +const path = require('path'); + +export default function resolveNodeModuleDir( + root: string, + packageName: string, +): string { + return path.join(root, 'node_modules', packageName); +} diff --git a/packages/cli/src/tools/config/__tests__/findDependencies-test.js b/packages/cli/src/tools/config/__tests__/findDependencies-test.js index d807cad03..779aeeb19 100644 --- a/packages/cli/src/tools/config/__tests__/findDependencies-test.js +++ b/packages/cli/src/tools/config/__tests__/findDependencies-test.js @@ -10,6 +10,8 @@ import { getTempDirectory, } from '../../../../../../jest/helpers'; +jest.mock('../resolveNodeModuleDir'); + beforeEach(() => { cleanup(DIR); jest.resetModules(); diff --git a/packages/cli/src/tools/config/__tests__/index-test.js b/packages/cli/src/tools/config/__tests__/index-test.js index bc4821f65..5a1953b3a 100644 --- a/packages/cli/src/tools/config/__tests__/index-test.js +++ b/packages/cli/src/tools/config/__tests__/index-test.js @@ -12,6 +12,8 @@ import { import {logger} from '@react-native-community/cli-tools'; +jest.mock('../resolveNodeModuleDir'); + const DIR = getTempDirectory('resolve_config_path_test'); // Removes string from all key/values within an object diff --git a/packages/cli/src/tools/config/index.js b/packages/cli/src/tools/config/index.js index ad3be6707..9c402b84e 100644 --- a/packages/cli/src/tools/config/index.js +++ b/packages/cli/src/tools/config/index.js @@ -19,6 +19,7 @@ import {type ConfigT} from 'types'; import assign from '../assign'; import merge from '../merge'; +import resolveNodeModuleDir from './resolveNodeModuleDir'; /** * Built-in platforms */ @@ -34,10 +35,10 @@ function loadConfig(projectRoot: string = process.cwd()): ConfigT { const finalConfig = findDependencies(projectRoot).reduce( (acc: ConfigT, dependencyName) => { - const root = path.join(projectRoot, 'node_modules', dependencyName); - + let root; let config; try { + root = resolveNodeModuleDir(projectRoot, dependencyName); config = readLegacyDependencyConfigFromDisk(root) || readDependencyConfigFromDisk(root); diff --git a/packages/cli/src/tools/config/resolveNodeModuleDir.js b/packages/cli/src/tools/config/resolveNodeModuleDir.js new file mode 100644 index 000000000..09a0e4917 --- /dev/null +++ b/packages/cli/src/tools/config/resolveNodeModuleDir.js @@ -0,0 +1,19 @@ +/** + * @flow + */ +import path from 'path'; + +/** + * Finds a path inside `node_modules` + */ +export default function resolveNodeModuleDir( + root: string, + packageName: string, +): string { + return path.dirname( + // $FlowIssue: Wrong `require.resolve` type definition + require.resolve(path.join(packageName, 'package.json'), { + paths: [root], + }), + ); +} diff --git a/packages/cli/src/tools/config/resolveReactNativePath.js b/packages/cli/src/tools/config/resolveReactNativePath.js index d814c2a6e..dbce4e5c8 100644 --- a/packages/cli/src/tools/config/resolveReactNativePath.js +++ b/packages/cli/src/tools/config/resolveReactNativePath.js @@ -1,21 +1,17 @@ /** * @flow */ -import path from 'path'; import {CLIError} from '@react-native-community/cli-tools'; +import resolveNodeModuleDir from './resolveNodeModuleDir'; + /** * Finds path to React Native inside `node_modules` or throws * an error otherwise. */ export default function resolveReactNativePath(root: string) { try { - return path.dirname( - // $FlowIssue: Wrong `require.resolve` type definition - require.resolve('react-native/package.json', { - paths: [root], - }), - ); + return resolveNodeModuleDir(root, 'react-native'); } catch (_ignored) { throw new CLIError(` Unable to find React Native files. Make sure "react-native" module is installed