Feature/move expo (#11712)
* feat(expo): move expo to main repo * feat(expo): add e2e expo tests and fix expo unit tests * feat(expo): add generated docs * feat(expo): update cz-config to add expo to commit message * feat(expo): add @nrwl/expo to e2e test setup * feat(expo): add expo to preset * feat(expo): add detox tests * feat(expo): update docs * feat(expo): fix e2e tests * feat(expo): upgrade expo to 46.0.10 * fix(expo): correct eas-cli build:info parameters names * fix(expo): add cleanupProject to e2e test
This commit is contained in:
parent
d90bdaec5f
commit
a411e85e42
@ -35,6 +35,7 @@ module.exports = {
|
|||||||
{ name: 'nxdev', description: 'anything related to docs infrastructure' },
|
{ name: 'nxdev', description: 'anything related to docs infrastructure' },
|
||||||
{ name: 'react', description: 'anything React specific' },
|
{ name: 'react', description: 'anything React specific' },
|
||||||
{ name: 'react-native', description: 'anything React Native specific' },
|
{ name: 'react-native', description: 'anything React Native specific' },
|
||||||
|
{ name: 'expo', description: 'anything Expo specific' },
|
||||||
{
|
{
|
||||||
name: 'repo',
|
name: 'repo',
|
||||||
description: 'anything related to managing the repo itself',
|
description: 'anything related to managing the repo itself',
|
||||||
|
|||||||
@ -113,7 +113,7 @@ Package manager to use
|
|||||||
|
|
||||||
Type: string
|
Type: string
|
||||||
|
|
||||||
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular", "angular-nest", "react", "react-express", "react-native", "next", "nest", "express"]. To build your own see https://nx.dev/packages/nx-plugin#preset
|
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular", "angular-nest", "react", "react-express", "react-native", "expo", "next", "nest", "express"]. To build your own see https://nx.dev/packages/nx-plugin#preset
|
||||||
|
|
||||||
### skipGit
|
### skipGit
|
||||||
|
|
||||||
|
|||||||
1161
docs/generated/packages/expo.json
Normal file
1161
docs/generated/packages/expo.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
"name": "create-nx-workspace",
|
"name": "create-nx-workspace",
|
||||||
"id": "create-nx-workspace",
|
"id": "create-nx-workspace",
|
||||||
"file": "generated/cli/create-nx-workspace",
|
"file": "generated/cli/create-nx-workspace",
|
||||||
"content": "---\ntitle: 'create-nx-workspace - CLI command'\ndescription: 'Create a new Nx workspace'\n---\n\n# create-nx-workspace\n\nCreate a new Nx workspace\n\n## Usage\n\n```bash\ncreate-nx-workspace [name] [options]\n```\n\nInstall `create-nx-workspace` globally to invoke the command directly, or use `npx create-nx-workspace`, `yarn create nx-workspace`, or `pnpx create-nx-workspace`.\n\n## Options\n\n### allPrompts\n\nType: boolean\n\nDefault: false\n\nShow all prompts\n\n### appName\n\nType: string\n\nThe name of the application when a preset with pregenerated app is selected\n\n### ci\n\nType: string\n\nChoices: [github, circleci, azure]\n\nGenerate a CI workflow file\n\n### cli\n\nType: string\n\nChoices: [nx, angular]\n\nCLI to power the Nx workspace\n\n### commit.email\n\nType: string\n\nE-mail of the committer\n\n### commit.message\n\nType: string\n\nDefault: Initial commit\n\nCommit message\n\n### commit.name\n\nType: string\n\nName of the committer\n\n### defaultBase\n\nType: string\n\nDefault: main\n\nDefault base to use for new projects\n\n### help\n\nType: boolean\n\nShow help\n\n### interactive\n\nType: boolean\n\nEnable interactive mode with presets\n\n### name\n\nType: string\n\nWorkspace name (e.g. org name)\n\n### nxCloud\n\nType: boolean\n\nEnable distributed caching to make your CI faster\n\n### packageManager\n\nType: string\n\nChoices: [npm, pnpm, yarn]\n\nDefault: npm\n\nPackage manager to use\n\n### preset\n\nType: string\n\nCustomizes the initial content of your workspace. Default presets include: [\"apps\", \"empty\", \"core\", \"npm\", \"ts\", \"web-components\", \"angular\", \"angular-nest\", \"react\", \"react-express\", \"react-native\", \"next\", \"nest\", \"express\"]. To build your own see https://nx.dev/packages/nx-plugin#preset\n\n### skipGit\n\nType: boolean\n\nDefault: false\n\nSkip initializing a git repository.\n\n### style\n\nType: string\n\nStyle option to be used when a preset with pregenerated app is selected\n\n### version\n\nType: boolean\n\nShow version number\n"
|
"content": "---\ntitle: 'create-nx-workspace - CLI command'\ndescription: 'Create a new Nx workspace'\n---\n\n# create-nx-workspace\n\nCreate a new Nx workspace\n\n## Usage\n\n```bash\ncreate-nx-workspace [name] [options]\n```\n\nInstall `create-nx-workspace` globally to invoke the command directly, or use `npx create-nx-workspace`, `yarn create nx-workspace`, or `pnpx create-nx-workspace`.\n\n## Options\n\n### allPrompts\n\nType: boolean\n\nDefault: false\n\nShow all prompts\n\n### appName\n\nType: string\n\nThe name of the application when a preset with pregenerated app is selected\n\n### ci\n\nType: string\n\nChoices: [github, circleci, azure]\n\nGenerate a CI workflow file\n\n### cli\n\nType: string\n\nChoices: [nx, angular]\n\nCLI to power the Nx workspace\n\n### commit.email\n\nType: string\n\nE-mail of the committer\n\n### commit.message\n\nType: string\n\nDefault: Initial commit\n\nCommit message\n\n### commit.name\n\nType: string\n\nName of the committer\n\n### defaultBase\n\nType: string\n\nDefault: main\n\nDefault base to use for new projects\n\n### help\n\nType: boolean\n\nShow help\n\n### interactive\n\nType: boolean\n\nEnable interactive mode with presets\n\n### name\n\nType: string\n\nWorkspace name (e.g. org name)\n\n### nxCloud\n\nType: boolean\n\nEnable distributed caching to make your CI faster\n\n### packageManager\n\nType: string\n\nChoices: [npm, pnpm, yarn]\n\nDefault: npm\n\nPackage manager to use\n\n### preset\n\nType: string\n\nCustomizes the initial content of your workspace. Default presets include: [\"apps\", \"empty\", \"core\", \"npm\", \"ts\", \"web-components\", \"angular\", \"angular-nest\", \"react\", \"react-express\", \"react-native\", \"expo\", \"next\", \"nest\", \"express\"]. To build your own see https://nx.dev/packages/nx-plugin#preset\n\n### skipGit\n\nType: boolean\n\nDefault: false\n\nSkip initializing a git repository.\n\n### style\n\nType: string\n\nStyle option to be used when a preset with pregenerated app is selected\n\n### version\n\nType: boolean\n\nShow version number\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "init",
|
"name": "init",
|
||||||
|
|||||||
@ -115,6 +115,33 @@
|
|||||||
"path": "generated/packages/eslint-plugin-nx.json",
|
"path": "generated/packages/eslint-plugin-nx.json",
|
||||||
"schemas": { "executors": [], "generators": [] }
|
"schemas": { "executors": [], "generators": [] }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "expo",
|
||||||
|
"packageName": "expo",
|
||||||
|
"description": "Expo Plugin for Nx",
|
||||||
|
"path": "generated/packages/expo.json",
|
||||||
|
"schemas": {
|
||||||
|
"executors": [
|
||||||
|
"update",
|
||||||
|
"build",
|
||||||
|
"build-list",
|
||||||
|
"download",
|
||||||
|
"build-ios",
|
||||||
|
"build-android",
|
||||||
|
"build-web",
|
||||||
|
"build-status",
|
||||||
|
"publish",
|
||||||
|
"publish-set",
|
||||||
|
"rollback",
|
||||||
|
"run",
|
||||||
|
"start",
|
||||||
|
"sync-deps",
|
||||||
|
"ensure-symlink",
|
||||||
|
"eject"
|
||||||
|
],
|
||||||
|
"generators": ["init", "application", "library", "component"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "express",
|
"name": "express",
|
||||||
"packageName": "express",
|
"packageName": "express",
|
||||||
|
|||||||
@ -13,12 +13,12 @@ describe('Detox', () => {
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
newProject();
|
newProject();
|
||||||
runCLI(
|
|
||||||
`generate @nrwl/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create files and run lint command', async () => {
|
it('should create files and run lint command for react-native apps', async () => {
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/react-native:app ${appName} --e2eTestRunner=detox --linter=eslint --install=false`
|
||||||
|
);
|
||||||
checkFilesExist(`apps/${appName}-e2e/.detoxrc.json`);
|
checkFilesExist(`apps/${appName}-e2e/.detoxrc.json`);
|
||||||
checkFilesExist(`apps/${appName}-e2e/tsconfig.json`);
|
checkFilesExist(`apps/${appName}-e2e/tsconfig.json`);
|
||||||
checkFilesExist(`apps/${appName}-e2e/tsconfig.e2e.json`);
|
checkFilesExist(`apps/${appName}-e2e/tsconfig.e2e.json`);
|
||||||
@ -29,6 +29,21 @@ describe('Detox', () => {
|
|||||||
expect(lintResults.combinedOutput).toContain('All files pass linting');
|
expect(lintResults.combinedOutput).toContain('All files pass linting');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create files and run lint command for expo apps', async () => {
|
||||||
|
const expoAppName = uniq('myapp');
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/expo:app ${expoAppName} --e2eTestRunner=detox --linter=eslint`
|
||||||
|
);
|
||||||
|
checkFilesExist(`apps/${expoAppName}-e2e/.detoxrc.json`);
|
||||||
|
checkFilesExist(`apps/${expoAppName}-e2e/tsconfig.json`);
|
||||||
|
checkFilesExist(`apps/${expoAppName}-e2e/tsconfig.e2e.json`);
|
||||||
|
checkFilesExist(`apps/${expoAppName}-e2e/test-setup.ts`);
|
||||||
|
checkFilesExist(`apps/${expoAppName}-e2e/src/app.spec.ts`);
|
||||||
|
|
||||||
|
const lintResults = await runCLIAsync(`lint ${expoAppName}-e2e`);
|
||||||
|
expect(lintResults.combinedOutput).toContain('All files pass linting');
|
||||||
|
});
|
||||||
|
|
||||||
describe('React Native Detox MACOS-Tests', () => {
|
describe('React Native Detox MACOS-Tests', () => {
|
||||||
if (isOSX()) {
|
if (isOSX()) {
|
||||||
it('should test ios MACOS-Tests', async () => {
|
it('should test ios MACOS-Tests', async () => {
|
||||||
|
|||||||
12
e2e/expo/jest.config.ts
Normal file
12
e2e/expo/jest.config.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||||
|
maxWorkers: 1,
|
||||||
|
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||||
|
displayName: 'e2e-expo',
|
||||||
|
testTimeout: 600000,
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
};
|
||||||
34
e2e/expo/project.json
Normal file
34
e2e/expo/project.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "e2e/expo",
|
||||||
|
"projectType": "application",
|
||||||
|
"targets": {
|
||||||
|
"e2e": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"options": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "yarn e2e-start-local-registry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "yarn e2e-build-package-publish"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "nx run-e2e-tests e2e-expo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parallel": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run-e2e-tests": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "e2e/expo/jest.config.ts",
|
||||||
|
"passWithNoTests": true,
|
||||||
|
"runInBand": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/e2e/expo"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"implicitDependencies": ["expo"]
|
||||||
|
}
|
||||||
45
e2e/expo/src/expo.test.ts
Normal file
45
e2e/expo/src/expo.test.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
cleanupProject,
|
||||||
|
expectTestsPass,
|
||||||
|
newProject,
|
||||||
|
runCLI,
|
||||||
|
runCLIAsync,
|
||||||
|
uniq,
|
||||||
|
updateFile,
|
||||||
|
} from '@nrwl/e2e/utils';
|
||||||
|
|
||||||
|
describe('expo', () => {
|
||||||
|
let proj: string;
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
() => (proj = newProject({ name: uniq('proj'), packageManager: 'npm' }))
|
||||||
|
);
|
||||||
|
afterEach(() => cleanupProject());
|
||||||
|
|
||||||
|
it('should test, lint', async () => {
|
||||||
|
const appName = uniq('my-app');
|
||||||
|
const libName = uniq('lib');
|
||||||
|
const componentName = uniq('component');
|
||||||
|
|
||||||
|
runCLI(`generate @nrwl/expo:application ${appName}`);
|
||||||
|
runCLI(`generate @nrwl/expo:library ${libName}`);
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/expo:component ${componentName} --project=${libName} --export`
|
||||||
|
);
|
||||||
|
expectTestsPass(await runCLIAsync(`test ${appName}`));
|
||||||
|
expectTestsPass(await runCLIAsync(`test ${libName}`));
|
||||||
|
|
||||||
|
updateFile(`apps/${appName}/src/app/App.tsx`, (content) => {
|
||||||
|
let updated = `// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport {${componentName}} from '${proj}/${libName}';\n${content}`;
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
|
||||||
|
expectTestsPass(await runCLIAsync(`test ${appName}`));
|
||||||
|
|
||||||
|
const appLintResults = await runCLIAsync(`lint ${appName}`);
|
||||||
|
expect(appLintResults.combinedOutput).toContain('All files pass linting.');
|
||||||
|
|
||||||
|
const libLintResults = await runCLIAsync(`lint ${libName}`);
|
||||||
|
expect(libLintResults.combinedOutput).toContain('All files pass linting.');
|
||||||
|
}, 1000000);
|
||||||
|
});
|
||||||
13
e2e/expo/tsconfig.json
Normal file
13
e2e/expo/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node", "jest"]
|
||||||
|
},
|
||||||
|
"include": [],
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
20
e2e/expo/tsconfig.spec.json
Normal file
20
e2e/expo/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.test.jsx",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"jest.config.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -319,6 +319,7 @@ export function newProject({
|
|||||||
`@nrwl/web`,
|
`@nrwl/web`,
|
||||||
`@nrwl/webpack`,
|
`@nrwl/webpack`,
|
||||||
`@nrwl/react-native`,
|
`@nrwl/react-native`,
|
||||||
|
`@nrwl/expo`,
|
||||||
];
|
];
|
||||||
packageInstall(packages.join(` `), projScope);
|
packageInstall(packages.join(` `), projScope);
|
||||||
|
|
||||||
|
|||||||
@ -190,6 +190,30 @@ describe('create-nx-workspace', () => {
|
|||||||
expectNoAngularDevkit();
|
expectNoAngularDevkit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to create react-native workspace', () => {
|
||||||
|
const wsName = uniq('react-native');
|
||||||
|
const appName = uniq('app');
|
||||||
|
runCreateWorkspace(wsName, {
|
||||||
|
preset: 'react-native',
|
||||||
|
appName,
|
||||||
|
packageManager: 'npm',
|
||||||
|
});
|
||||||
|
|
||||||
|
expectNoAngularDevkit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to create an expo workspace', () => {
|
||||||
|
const wsName = uniq('expo');
|
||||||
|
const appName = uniq('app');
|
||||||
|
runCreateWorkspace(wsName, {
|
||||||
|
preset: 'expo',
|
||||||
|
appName,
|
||||||
|
packageManager: 'npm',
|
||||||
|
});
|
||||||
|
|
||||||
|
expectNoAngularDevkit();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to create a workspace with a custom base branch and HEAD', () => {
|
it('should be able to create a workspace with a custom base branch and HEAD', () => {
|
||||||
const wsName = uniq('branch');
|
const wsName = uniq('branch');
|
||||||
runCreateWorkspace(wsName, {
|
runCreateWorkspace(wsName, {
|
||||||
|
|||||||
@ -241,6 +241,7 @@
|
|||||||
"styled-components": "5.0.0",
|
"styled-components": "5.0.0",
|
||||||
"stylus": "^0.55.0",
|
"stylus": "^0.55.0",
|
||||||
"stylus-loader": "^6.2.0",
|
"stylus-loader": "^6.2.0",
|
||||||
|
"tar-fs": "^2.1.1",
|
||||||
"tar-stream": "~2.2.0",
|
"tar-stream": "~2.2.0",
|
||||||
"tcp-port-used": "^1.0.2",
|
"tcp-port-used": "^1.0.2",
|
||||||
"terser-webpack-plugin": "^5.3.3",
|
"terser-webpack-plugin": "^5.3.3",
|
||||||
|
|||||||
@ -56,6 +56,7 @@ enum Preset {
|
|||||||
React = 'react',
|
React = 'react',
|
||||||
ReactWithExpress = 'react-express',
|
ReactWithExpress = 'react-express',
|
||||||
ReactNative = 'react-native',
|
ReactNative = 'react-native',
|
||||||
|
Expo = 'expo',
|
||||||
NextJs = 'next',
|
NextJs = 'next',
|
||||||
Nest = 'nest',
|
Nest = 'nest',
|
||||||
Express = 'express',
|
Express = 'express',
|
||||||
@ -141,6 +142,10 @@ const presetOptions: { name: Preset; message: string }[] = [
|
|||||||
message:
|
message:
|
||||||
'react-native [a workspace with a single React Native application]',
|
'react-native [a workspace with a single React Native application]',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: Preset.Expo,
|
||||||
|
message: 'expo [a workspace with a single Expo application]',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: Preset.ReactWithExpress,
|
name: Preset.ReactWithExpress,
|
||||||
message:
|
message:
|
||||||
@ -625,7 +630,8 @@ async function determineStyle(
|
|||||||
preset === Preset.NPM ||
|
preset === Preset.NPM ||
|
||||||
preset === Preset.Nest ||
|
preset === Preset.Nest ||
|
||||||
preset === Preset.Express ||
|
preset === Preset.Express ||
|
||||||
preset === Preset.ReactNative
|
preset === Preset.ReactNative ||
|
||||||
|
preset === Preset.Expo
|
||||||
) {
|
) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|||||||
19
packages/expo/.eslintrc.json
Normal file
19
packages/expo/.eslintrc.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../.eslintrc",
|
||||||
|
"rules": {},
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"./package.json",
|
||||||
|
"./generators.json",
|
||||||
|
"./executors.json",
|
||||||
|
"./migrations.json"
|
||||||
|
],
|
||||||
|
"parser": "jsonc-eslint-parser",
|
||||||
|
"rules": {
|
||||||
|
"@nrwl/nx/nx-plugin-checks": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
13
packages/expo/README.md
Normal file
13
packages/expo/README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
|
||||||
|
|
||||||
|
{{links}}
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
# Nx: Smart, Fast and Extensible Build System
|
||||||
|
|
||||||
|
Nx is a next generation build system with first class monorepo support and powerful integrations.
|
||||||
|
|
||||||
|
This package is a [Expo plugin for Nx](https://nx.dev/expo/overview).
|
||||||
|
|
||||||
|
{{content}}
|
||||||
0
packages/expo/collection.json
Normal file
0
packages/expo/collection.json
Normal file
166
packages/expo/executors.json
Normal file
166
packages/expo/executors.json
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
{
|
||||||
|
"executors": {
|
||||||
|
"update": {
|
||||||
|
"implementation": "./src/executors/update/update.impl",
|
||||||
|
"schema": "./src/executors/update/schema.json",
|
||||||
|
"description": "Start an EAS update for your expo project"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"implementation": "./src/executors/build/build.impl",
|
||||||
|
"schema": "./src/executors/build/schema.json",
|
||||||
|
"description": "Start an EAS build for your expo project"
|
||||||
|
},
|
||||||
|
"build-list": {
|
||||||
|
"implementation": "./src/executors/build-list/build-list.impl",
|
||||||
|
"schema": "./src/executors/build-list/schema.json",
|
||||||
|
"description": "List all EAS builds for your Expo project"
|
||||||
|
},
|
||||||
|
"download": {
|
||||||
|
"implementation": "./src/executors/download/download.impl",
|
||||||
|
"schema": "./src/executors/download/schema.json",
|
||||||
|
"description": "Download an EAS build"
|
||||||
|
},
|
||||||
|
"build-ios": {
|
||||||
|
"implementation": "./src/executors/build-ios/build-ios.impl",
|
||||||
|
"schema": "./src/executors/build-ios/schema.json",
|
||||||
|
"description": "Build and sign a standalone IPA for the Apple App Store"
|
||||||
|
},
|
||||||
|
"build-android": {
|
||||||
|
"implementation": "./src/executors/build-android/build-android.impl",
|
||||||
|
"schema": "./src/executors/build-android/schema.json",
|
||||||
|
"description": "Build and sign a standalone APK or App Bundle for the Google Play Store"
|
||||||
|
},
|
||||||
|
"build-web": {
|
||||||
|
"implementation": "./src/executors/build-web/build-web.impl",
|
||||||
|
"schema": "./src/executors/build-web/schema.json",
|
||||||
|
"description": "Build the web app for production"
|
||||||
|
},
|
||||||
|
"build-status": {
|
||||||
|
"implementation": "./src/executors/build-status/build-status.impl",
|
||||||
|
"schema": "./src/executors/build-status/schema.json",
|
||||||
|
"description": "Get the status of the latest build for the project"
|
||||||
|
},
|
||||||
|
"publish": {
|
||||||
|
"implementation": "./src/executors/publish/publish.impl",
|
||||||
|
"schema": "./src/executors/publish/schema.json",
|
||||||
|
"description": "Deploy a project to Expo hosting"
|
||||||
|
},
|
||||||
|
"publish-set": {
|
||||||
|
"implementation": "./src/executors/publish-set/publish-set.impl",
|
||||||
|
"schema": "./src/executors/publish-set/schema.json",
|
||||||
|
"description": "Specify the channel to serve a published release"
|
||||||
|
},
|
||||||
|
"rollback": {
|
||||||
|
"implementation": "./src/executors/rollback/rollback.impl",
|
||||||
|
"schema": "./src/executors/rollback/schema.json",
|
||||||
|
"description": "Undo an update to a channel"
|
||||||
|
},
|
||||||
|
"run": {
|
||||||
|
"implementation": "./src/executors/run/run.impl",
|
||||||
|
"schema": "./src/executors/run/schema.json",
|
||||||
|
"description": "Run the Android app binary locally or run the iOS app binary locally"
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"implementation": "./src/executors/start/start.impl",
|
||||||
|
"schema": "./src/executors/start/schema.json",
|
||||||
|
"description": "Start a local dev server for the app or start a Webpack dev server for the web app"
|
||||||
|
},
|
||||||
|
"sync-deps": {
|
||||||
|
"implementation": "./src/executors/sync-deps/sync-deps.impl",
|
||||||
|
"schema": "./src/executors/sync-deps/schema.json",
|
||||||
|
"description": "Syncs dependencies to package.json (required for autolinking)."
|
||||||
|
},
|
||||||
|
"ensure-symlink": {
|
||||||
|
"implementation": "./src/executors/ensure-symlink/ensure-symlink.impl",
|
||||||
|
"schema": "./src/executors/ensure-symlink/schema.json",
|
||||||
|
"description": "Ensure workspace node_modules is symlink under app's node_modules folder."
|
||||||
|
},
|
||||||
|
"eject": {
|
||||||
|
"implementation": "./src/executors/eject/eject.impl",
|
||||||
|
"schema": "./src/executors/eject/schema.json",
|
||||||
|
"description": "Create native iOS and Android project files."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"builders": {
|
||||||
|
"update": {
|
||||||
|
"implementation": "./src/executors/update/compat",
|
||||||
|
"schema": "./src/executors/update/schema.json",
|
||||||
|
"description": "Start an EAS update for your expo project"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"implementation": "./src/executors/build/compat",
|
||||||
|
"schema": "./src/executors/build/schema.json",
|
||||||
|
"description": "Start an EAS build for your expo project"
|
||||||
|
},
|
||||||
|
"build-list": {
|
||||||
|
"implementation": "./src/executors/build-list/compat",
|
||||||
|
"schema": "./src/executors/build-list/schema.json",
|
||||||
|
"description": "List all EAS builds for your Expo project"
|
||||||
|
},
|
||||||
|
"download": {
|
||||||
|
"implementation": "./src/executors/download/compat",
|
||||||
|
"schema": "./src/executors/download/schema.json",
|
||||||
|
"description": "Download an EAS build"
|
||||||
|
},
|
||||||
|
"build-ios": {
|
||||||
|
"implementation": "./src/executors/build-ios/compat",
|
||||||
|
"schema": "./src/executors/build-ios/schema.json",
|
||||||
|
"description": "Build and sign a standalone IPA for the Apple App Store"
|
||||||
|
},
|
||||||
|
"build-android": {
|
||||||
|
"implementation": "./src/executors/build-android/compat",
|
||||||
|
"schema": "./src/executors/build-android/schema.json",
|
||||||
|
"description": "Build and sign a standalone APK or App Bundle for the Google Play Store"
|
||||||
|
},
|
||||||
|
"build-web": {
|
||||||
|
"implementation": "./src/executors/build-web/compat",
|
||||||
|
"schema": "./src/executors/build-web/schema.json",
|
||||||
|
"description": "Build the web app for production"
|
||||||
|
},
|
||||||
|
"build-status": {
|
||||||
|
"implementation": "./src/executors/build-status/compat",
|
||||||
|
"schema": "./src/executors/build-status/schema.json",
|
||||||
|
"description": "Get the status of the latest build for the project"
|
||||||
|
},
|
||||||
|
"publish": {
|
||||||
|
"implementation": "./src/executors/publish/compat",
|
||||||
|
"schema": "./src/executors/publish/schema.json",
|
||||||
|
"description": "Deploy a project to Expo hosting"
|
||||||
|
},
|
||||||
|
"publish-set": {
|
||||||
|
"implementation": "./src/executors/publish-set/compact",
|
||||||
|
"schema": "./src/executors/publish-set/schema.json",
|
||||||
|
"description": "Specify the channel to serve a published release"
|
||||||
|
},
|
||||||
|
"rollback": {
|
||||||
|
"implementation": "./src/executors/rollback/compact",
|
||||||
|
"schema": "./src/executors/rollback/schema.json",
|
||||||
|
"description": "Undo an update to a channel"
|
||||||
|
},
|
||||||
|
"run": {
|
||||||
|
"implementation": "./src/executors/run/compat",
|
||||||
|
"schema": "./src/executors/run/schema.json",
|
||||||
|
"description": "Run the Android app binary locally or run the iOS app binary locally"
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"implementation": "./src/executors/start/compat",
|
||||||
|
"schema": "./src/executors/start/schema.json",
|
||||||
|
"description": "Start a local dev server for the app or start a Webpack dev server for the web app"
|
||||||
|
},
|
||||||
|
"sync-deps": {
|
||||||
|
"implementation": "./src/executors/sync-deps/compat",
|
||||||
|
"schema": "./src/executors/sync-deps/schema.json",
|
||||||
|
"description": "Syncs dependencies to package.json (required for autolinking)."
|
||||||
|
},
|
||||||
|
"ensure-symlink": {
|
||||||
|
"implementation": "./src/executors/ensure-symlink/compat",
|
||||||
|
"schema": "./src/executors/ensure-symlink/schema.json",
|
||||||
|
"description": "Ensure workspace node_modules is symlink under app's node_modules folder."
|
||||||
|
},
|
||||||
|
"eject": {
|
||||||
|
"implementation": "./src/executors/eject/compat",
|
||||||
|
"schema": "./src/executors/eject/schema.json",
|
||||||
|
"description": "Create native iOS and Android project files."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
packages/expo/generators.json
Normal file
61
packages/expo/generators.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"name": "Nx Expo",
|
||||||
|
"version": "0.1",
|
||||||
|
"extends": ["@nrwl/workspace"],
|
||||||
|
"schematics": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#expoInitSchematic",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initialize the @nrwl/expo plugin",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"factory": "./src/generators/application/application#expoApplicationSchematic",
|
||||||
|
"schema": "./src/generators/application/schema.json",
|
||||||
|
"aliases": ["app"],
|
||||||
|
"x-type": "application",
|
||||||
|
"description": "Create an application"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"factory": "./src/generators/library/library#expoLibrarySchematic",
|
||||||
|
"schema": "./src/generators/library/schema.json",
|
||||||
|
"aliases": ["lib"],
|
||||||
|
"x-type": "library",
|
||||||
|
"description": "Create a library"
|
||||||
|
},
|
||||||
|
"component": {
|
||||||
|
"factory": "./src/generators/component/component#expoComponentSchematic",
|
||||||
|
"schema": "./src/generators/component/schema.json",
|
||||||
|
"description": "Create a component",
|
||||||
|
"aliases": ["c"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"generators": {
|
||||||
|
"init": {
|
||||||
|
"factory": "./src/generators/init/init#expoInitGenerator",
|
||||||
|
"schema": "./src/generators/init/schema.json",
|
||||||
|
"description": "Initialize the @nrwl/expo plugin",
|
||||||
|
"hidden": true
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"factory": "./src/generators/application/application#expoApplicationGenerator",
|
||||||
|
"schema": "./src/generators/application/schema.json",
|
||||||
|
"aliases": ["app"],
|
||||||
|
"x-type": "application",
|
||||||
|
"description": "Create an application"
|
||||||
|
},
|
||||||
|
"library": {
|
||||||
|
"factory": "./src/generators/library/library#expoLibraryGenerator",
|
||||||
|
"schema": "./src/generators/library/schema.json",
|
||||||
|
"aliases": ["lib"],
|
||||||
|
"x-type": "library",
|
||||||
|
"description": "Create a library"
|
||||||
|
},
|
||||||
|
"component": {
|
||||||
|
"factory": "./src/generators/component/component#expoComponentGenerator",
|
||||||
|
"schema": "./src/generators/component/schema.json",
|
||||||
|
"description": "Create a component",
|
||||||
|
"aliases": ["c"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/expo/index.ts
Normal file
4
packages/expo/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { expoInitGenerator } from './src/generators/init/init';
|
||||||
|
export { expoApplicationGenerator } from './src/generators/application/application';
|
||||||
|
export { withNxMetro } from './plugins/with-nx-metro';
|
||||||
|
export { withNxWebpack } from './plugins/with-nx-webpack';
|
||||||
13
packages/expo/jest.config.ts
Normal file
13
packages/expo/jest.config.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html', 'json'],
|
||||||
|
globals: {
|
||||||
|
'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' },
|
||||||
|
},
|
||||||
|
displayName: 'expo',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
};
|
||||||
415
packages/expo/migrations.json
Normal file
415
packages/expo/migrations.json
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
{
|
||||||
|
"schematics": {
|
||||||
|
"add-project-root-metro-config-14-0-0": {
|
||||||
|
"version": "14.0.1-beta.0",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Add projectRoot option in metro.config.js",
|
||||||
|
"factory": "./src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0"
|
||||||
|
},
|
||||||
|
"add-eject-target-14-1-2": {
|
||||||
|
"version": "14.1.2-beta.0",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Add target eject for expo projects in project.json",
|
||||||
|
"factory": "./src/migrations/update-14-1-2/add-eject-target-14-1-2"
|
||||||
|
},
|
||||||
|
"add-build-target-14-4-3": {
|
||||||
|
"version": "14.4.3-beta.0",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Add target build and build-list for expo projects in project.json",
|
||||||
|
"factory": "./src/migrations/update-14-4-3/add-eas-build-target"
|
||||||
|
},
|
||||||
|
"add-update-target-14-5-1": {
|
||||||
|
"version": "14.5.1-beta.0",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Add target update for expo projects in project.json",
|
||||||
|
"factory": "./src/migrations/update-14-5-1/add-eas-update-target"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packageJsonUpdates": {
|
||||||
|
"13.8.6": {
|
||||||
|
"version": "13.8.6-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.0.0": {
|
||||||
|
"version": "14.0.0-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"babel-preset-expo": {
|
||||||
|
"version": "~9.0.2",
|
||||||
|
"alwaysAddToPackageJson": false,
|
||||||
|
"addToPackageJson": "devDependencies"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.0.1": {
|
||||||
|
"version": "14.0.1-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.4.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.0.2": {
|
||||||
|
"version": "14.0.2-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"metro-resolver": {
|
||||||
|
"version": "0.70.2",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-dev-client": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@expo/metro-config": {
|
||||||
|
"version": "0.3.16",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-updates": {
|
||||||
|
"version": "~0.11.7",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.1.1": {
|
||||||
|
"version": "14.1.1-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "45.0.4",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-dev-client": {
|
||||||
|
"version": "~0.9.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-status-bar": {
|
||||||
|
"version": "~1.3.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@expo/metro-config": {
|
||||||
|
"version": "0.3.17",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-splash-screen": {
|
||||||
|
"version": "0.15.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-updates": {
|
||||||
|
"version": "~0.13.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"jest-expo": {
|
||||||
|
"version": "45.0.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.4.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"babel-preset-expo": {
|
||||||
|
"version": "~9.1.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"version": "0.68.2",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@types/react-native": {
|
||||||
|
"version": "0.67.7",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-web": {
|
||||||
|
"version": "0.17.7",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-gesture-handler": {
|
||||||
|
"version": "~2.2.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-reanimated": {
|
||||||
|
"version": "~2.8.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-safe-area-context": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-screens": {
|
||||||
|
"version": "~3.11.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-svg": {
|
||||||
|
"version": "12.3.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-resolver": {
|
||||||
|
"version": "0.70.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@testing-library/react-native": {
|
||||||
|
"version": "9.1.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@testing-library/jest-native": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.1.2": {
|
||||||
|
"version": "14.1.2-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "45.0.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.4.9",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-resolver": {
|
||||||
|
"version": "0.71.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-babel-register": {
|
||||||
|
"version": "0.71.0",
|
||||||
|
"alwaysAddToPackageJson": false,
|
||||||
|
"addToPackageJson": "devDependencies"
|
||||||
|
},
|
||||||
|
"react-test-renderer": {
|
||||||
|
"version": "18.1.0",
|
||||||
|
"alwaysAddToPackageJson": false,
|
||||||
|
"addToPackageJson": "devDependencies"
|
||||||
|
},
|
||||||
|
"expo-updates": {
|
||||||
|
"version": "~0.13.2",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@types/react-native": {
|
||||||
|
"version": "0.67.8",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.2.3": {
|
||||||
|
"version": "14.2.3-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo-dev-client": {
|
||||||
|
"version": "~0.10.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-structured-headers": {
|
||||||
|
"version": "~2.2.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.2.4": {
|
||||||
|
"version": "14.2.4-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo-dev-client": {
|
||||||
|
"version": "~1.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.3.2": {
|
||||||
|
"version": "14.3.2-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "45.0.6",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.4.11",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@types/react-native": {
|
||||||
|
"version": "0.68.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.4.3": {
|
||||||
|
"version": "14.4.3-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"eas-cli": {
|
||||||
|
"version": "0.55.1",
|
||||||
|
"alwaysAddToPackageJson": false,
|
||||||
|
"addToPackageJson": "devDependencies"
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "5.5.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.5.1": {
|
||||||
|
"version": "14.5.1-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "46.0.2",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-dev-client": {
|
||||||
|
"version": "~1.1.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-status-bar": {
|
||||||
|
"version": "~1.4.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@expo/metro-config": {
|
||||||
|
"version": "0.3.21",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-splash-screen": {
|
||||||
|
"version": "~0.16.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-updates": {
|
||||||
|
"version": "~0.14.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"jest-expo": {
|
||||||
|
"version": "46.0.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"eas-cli": {
|
||||||
|
"version": "0.57.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"babel-preset-expo": {
|
||||||
|
"version": "~9.2.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"version": "0.69.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@types/react-native": {
|
||||||
|
"version": "0.69.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-web": {
|
||||||
|
"version": "~0.18.7",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-gesture-handler": {
|
||||||
|
"version": "~2.5.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-reanimated": {
|
||||||
|
"version": "~2.9.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-safe-area-context": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-screens": {
|
||||||
|
"version": "~3.15.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-svg": {
|
||||||
|
"version": "12.4.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@svgr/webpack": {
|
||||||
|
"version": "^6.3.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-resolver": {
|
||||||
|
"version": "0.72.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-babel-register": {
|
||||||
|
"version": "0.72.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@testing-library/react-native": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.5.2": {
|
||||||
|
"version": "14.5.2-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "46.0.9",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"expo-cli": {
|
||||||
|
"version": "6.0.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"eas-cli": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"version": "0.69.4",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-svg": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro-resolver": {
|
||||||
|
"version": "0.72.1",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@testing-library/jest-native": {
|
||||||
|
"version": "4.0.11",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@expo/metro-config": {
|
||||||
|
"version": "0.3.22",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14.5.3": {
|
||||||
|
"version": "14.5.3-beta.0",
|
||||||
|
"packages": {
|
||||||
|
"expo": {
|
||||||
|
"version": "46.0.10",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"eas-cli": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"version": "0.69.5",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"@types/react-native": {
|
||||||
|
"version": "0.69.8",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"react-native-svg": {
|
||||||
|
"version": "13.1.0",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
},
|
||||||
|
"metro": {
|
||||||
|
"version": "0.72.2",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
packages/expo/package.json
Normal file
54
packages/expo/package.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "@nrwl/expo",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Expo Plugin for Nx",
|
||||||
|
"keywords": [
|
||||||
|
"Monorepo",
|
||||||
|
"Expo",
|
||||||
|
"React",
|
||||||
|
"Web",
|
||||||
|
"Jest",
|
||||||
|
"Native",
|
||||||
|
"CLI"
|
||||||
|
],
|
||||||
|
"homepage": "https://nx.dev",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nrwl/nx/issues"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/nrwl/nx.git",
|
||||||
|
"directory": "packages/expo"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Victor Savkin",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"dependencies": {
|
||||||
|
"@nrwl/detox": "file:../detox",
|
||||||
|
"@nrwl/devkit": "file:../devkit",
|
||||||
|
"@nrwl/jest": "file:../jest",
|
||||||
|
"@nrwl/linter": "file:../linter",
|
||||||
|
"@nrwl/react": "file:../react",
|
||||||
|
"@nrwl/web": "file:../web",
|
||||||
|
"@nrwl/workspace": "file:../workspace",
|
||||||
|
"@svgr/webpack": "^6.1.2",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"enhanced-resolve": "^5.8.3",
|
||||||
|
"fs-extra": "^10.1.0",
|
||||||
|
"metro-resolver": "^0.72.2",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"tar-fs": "^2.1.1",
|
||||||
|
"tsconfig-paths": "^3.9.0",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^3.5.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "^46.0.10"
|
||||||
|
},
|
||||||
|
"builders": "./executors.json",
|
||||||
|
"ng-update": {
|
||||||
|
"requirements": {},
|
||||||
|
"migrations": "./migrations.json"
|
||||||
|
},
|
||||||
|
"schematics": "./generators.json"
|
||||||
|
}
|
||||||
3
packages/expo/plugins/jest/svg-mock.ts
Normal file
3
packages/expo/plugins/jest/svg-mock.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// From https://github.com/kristerkari/react-native-svg-transformer#usage-with-jest
|
||||||
|
module.exports = 'SvgMock';
|
||||||
|
module.exports.ReactComponent = 'SvgMock';
|
||||||
183
packages/expo/plugins/metro-resolver.ts
Normal file
183
packages/expo/plugins/metro-resolver.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import * as metroResolver from 'metro-resolver';
|
||||||
|
import type { MatchPath } from 'tsconfig-paths';
|
||||||
|
import { createMatchPath, loadConfig } from 'tsconfig-paths';
|
||||||
|
import * as chalk from 'chalk';
|
||||||
|
import { CachedInputFileSystem, ResolverFactory } from 'enhanced-resolve';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { workspaceRoot } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use tsconfig to resolve additional workspace libs.
|
||||||
|
*
|
||||||
|
* This resolve function requires projectRoot to be set to
|
||||||
|
* workspace root in order modules and assets to be registered and watched.
|
||||||
|
*/
|
||||||
|
export function getResolveRequest(extensions: string[]) {
|
||||||
|
return function (
|
||||||
|
_context: any,
|
||||||
|
realModuleName: string,
|
||||||
|
platform: string | null
|
||||||
|
) {
|
||||||
|
const debug = process.env.NX_REACT_NATIVE_DEBUG === 'true';
|
||||||
|
|
||||||
|
if (debug) console.log(chalk.cyan(`[Nx] Resolving: ${realModuleName}`));
|
||||||
|
|
||||||
|
const { resolveRequest, ...context } = _context;
|
||||||
|
|
||||||
|
const resolvedPath =
|
||||||
|
defaultMetroResolver(context, realModuleName, platform, debug) ||
|
||||||
|
tsconfigPathsResolver(
|
||||||
|
context,
|
||||||
|
extensions,
|
||||||
|
realModuleName,
|
||||||
|
platform,
|
||||||
|
debug
|
||||||
|
) ||
|
||||||
|
pnpmResolver(extensions, context, realModuleName, debug);
|
||||||
|
if (resolvedPath) {
|
||||||
|
return resolvedPath;
|
||||||
|
}
|
||||||
|
throw new Error(`Cannot resolve ${chalk.bold(realModuleName)}`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function try to resolve path using metro's default resolver
|
||||||
|
* @returns path if resolved, else undefined
|
||||||
|
*/
|
||||||
|
function defaultMetroResolver(
|
||||||
|
context: any,
|
||||||
|
realModuleName: string,
|
||||||
|
platform: string,
|
||||||
|
debug: boolean
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return metroResolver.resolve(context, realModuleName, platform);
|
||||||
|
} catch {
|
||||||
|
if (debug)
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
`[Nx] Unable to resolve with default Metro resolver: ${realModuleName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This resolver try to resolve module for pnpm.
|
||||||
|
* @returns path if resolved, else undefined
|
||||||
|
* This pnpm resolver is inspired from https://github.com/vjpr/pnpm-react-native-example/blob/main/packages/pnpm-expo-helper/util/make-resolver.js
|
||||||
|
*/
|
||||||
|
function pnpmResolver(
|
||||||
|
extensions: string[],
|
||||||
|
context: any,
|
||||||
|
realModuleName: string,
|
||||||
|
debug: boolean
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const pnpmResolve = getPnpmResolver(extensions);
|
||||||
|
const lookupStartPath = dirname(context.originModulePath);
|
||||||
|
const filePath = pnpmResolve.resolveSync(
|
||||||
|
{},
|
||||||
|
lookupStartPath,
|
||||||
|
realModuleName
|
||||||
|
);
|
||||||
|
if (filePath) {
|
||||||
|
return { type: 'sourceFile', filePath };
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if (debug)
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
`[Nx] Unable to resolve with default PNPM resolver: ${realModuleName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function try to resolve files that are specified in tsconfig's paths
|
||||||
|
* @returns path if resolved, else undefined
|
||||||
|
*/
|
||||||
|
function tsconfigPathsResolver(
|
||||||
|
context: any,
|
||||||
|
extensions: string[],
|
||||||
|
realModuleName: string,
|
||||||
|
platform: string,
|
||||||
|
debug: boolean
|
||||||
|
) {
|
||||||
|
const tsConfigPathMatcher = getMatcher(debug);
|
||||||
|
const match = tsConfigPathMatcher(
|
||||||
|
realModuleName,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
extensions.map((ext) => `.${ext}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
return metroResolver.resolve(context, match, platform);
|
||||||
|
} else {
|
||||||
|
if (debug) {
|
||||||
|
console.log(
|
||||||
|
chalk.red(`[Nx] Failed to resolve ${chalk.bold(realModuleName)}`)
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
`[Nx] The following tsconfig paths was used:\n:${chalk.bold(
|
||||||
|
JSON.stringify(paths, null, 2)
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let matcher: MatchPath;
|
||||||
|
let absoluteBaseUrl: string;
|
||||||
|
let paths: Record<string, string[]>;
|
||||||
|
|
||||||
|
function getMatcher(debug: boolean) {
|
||||||
|
if (!matcher) {
|
||||||
|
const result = loadConfig();
|
||||||
|
if (result.resultType === 'success') {
|
||||||
|
absoluteBaseUrl = result.absoluteBaseUrl;
|
||||||
|
paths = result.paths;
|
||||||
|
if (debug) {
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(`[Nx] Located tsconfig at ${chalk.bold(absoluteBaseUrl)}`)
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
`[Nx] Found the following paths:\n:${chalk.bold(
|
||||||
|
JSON.stringify(paths, null, 2)
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
matcher = createMatchPath(absoluteBaseUrl, paths);
|
||||||
|
} else {
|
||||||
|
console.log(chalk.cyan(`[Nx] Failed to locate tsconfig}`));
|
||||||
|
throw new Error(`Could not load tsconfig for project`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns resolver for pnpm.
|
||||||
|
* It is inspired form https://github.com/vjpr/pnpm-expo-example/blob/main/packages/pnpm-expo-helper/util/make-resolver.js.
|
||||||
|
*/
|
||||||
|
let resolver;
|
||||||
|
function getPnpmResolver(extensions: string[]) {
|
||||||
|
if (!resolver) {
|
||||||
|
const fileSystem = new CachedInputFileSystem(fs, 4000);
|
||||||
|
resolver = ResolverFactory.createResolver({
|
||||||
|
fileSystem,
|
||||||
|
extensions: extensions.map((extension) => '.' + extension),
|
||||||
|
useSyncFileSystemCalls: true,
|
||||||
|
modules: [join(workspaceRoot, 'node_modules'), 'node_modules'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
40
packages/expo/plugins/with-nx-metro.ts
Normal file
40
packages/expo/plugins/with-nx-metro.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { workspaceLayout, workspaceRoot } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { existsSync } from 'fs-extra';
|
||||||
|
|
||||||
|
import { getResolveRequest } from './metro-resolver';
|
||||||
|
|
||||||
|
interface WithNxOptions {
|
||||||
|
debug?: boolean;
|
||||||
|
extensions?: string[];
|
||||||
|
projectRoot?: string;
|
||||||
|
watchFolders?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withNxMetro(config: any, opts: WithNxOptions = {}) {
|
||||||
|
const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json'];
|
||||||
|
if (opts.debug) process.env.NX_REACT_NATIVE_DEBUG = 'true';
|
||||||
|
if (opts.extensions) extensions.push(...opts.extensions);
|
||||||
|
|
||||||
|
config.projectRoot = opts.projectRoot || workspaceRoot;
|
||||||
|
|
||||||
|
// Add support for paths specified by tsconfig
|
||||||
|
config.resolver = {
|
||||||
|
...config.resolver,
|
||||||
|
resolveRequest: getResolveRequest(extensions),
|
||||||
|
};
|
||||||
|
|
||||||
|
let watchFolders = config.watchFolders || [];
|
||||||
|
watchFolders = watchFolders.concat([
|
||||||
|
join(workspaceRoot, 'node_modules'),
|
||||||
|
join(workspaceRoot, workspaceLayout().libsDir),
|
||||||
|
]);
|
||||||
|
if (opts.watchFolders?.length) {
|
||||||
|
watchFolders = watchFolders.concat(opts.watchFolders);
|
||||||
|
}
|
||||||
|
|
||||||
|
watchFolders = watchFolders.filter((folder) => existsSync(folder));
|
||||||
|
config.watchFolders = watchFolders;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
100
packages/expo/plugins/with-nx-webpack.ts
Normal file
100
packages/expo/plugins/with-nx-webpack.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function add addtional rules to expo's webpack config to make expo web working
|
||||||
|
*/
|
||||||
|
export async function withNxWebpack(config) {
|
||||||
|
// add additional rule to load files under libs
|
||||||
|
const rules = config.module.rules[1]?.oneOf;
|
||||||
|
if (rules) {
|
||||||
|
rules.push({
|
||||||
|
test: /\.(mjs|[jt]sx?)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: require.resolve('@nrwl/web/src/utils/web-babel-loader.js'),
|
||||||
|
options: {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@nrwl/react/babel',
|
||||||
|
{
|
||||||
|
runtime: 'automatic',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// svg rule from https://github.com/kristerkari/react-native-svg-transformer/issues/135#issuecomment-1008310514
|
||||||
|
rules.unshift({
|
||||||
|
test: /\.svg$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: require.resolve('@svgr/webpack'),
|
||||||
|
options: {
|
||||||
|
svgoConfig: {
|
||||||
|
plugins: {
|
||||||
|
cleanupAttrs: true,
|
||||||
|
cleanupEnableBackground: true,
|
||||||
|
cleanupIDs: true,
|
||||||
|
cleanupListOfValues: true,
|
||||||
|
cleanupNumericValues: true,
|
||||||
|
collapseGroups: true,
|
||||||
|
convertEllipseToCircle: true,
|
||||||
|
convertPathData: true,
|
||||||
|
convertShapeToPath: true,
|
||||||
|
convertStyleToAttrs: true,
|
||||||
|
convertTransform: true,
|
||||||
|
inlineStyles: true,
|
||||||
|
mergePaths: true,
|
||||||
|
minifyStyles: true,
|
||||||
|
moveElemsAttrsToGroup: true,
|
||||||
|
moveGroupAttrsToElems: true,
|
||||||
|
removeComments: true,
|
||||||
|
removeDesc: true,
|
||||||
|
removeDimensions: false,
|
||||||
|
removeDoctype: true,
|
||||||
|
removeEditorsNSData: true,
|
||||||
|
removeEmptyAttrs: true,
|
||||||
|
removeEmptyContainers: true,
|
||||||
|
removeEmptyText: true,
|
||||||
|
removeHiddenElems: true,
|
||||||
|
removeMetadata: true,
|
||||||
|
removeNonInheritableGroupAttrs: true,
|
||||||
|
removeRasterImages: true,
|
||||||
|
removeScriptElement: false,
|
||||||
|
removeStyleElement: false,
|
||||||
|
removeTitle: true,
|
||||||
|
removeUnknownsAndDefaults: true,
|
||||||
|
removeUnusedNS: true,
|
||||||
|
removeUselessDefs: true,
|
||||||
|
removeUselessStrokeAndFill: true,
|
||||||
|
removeViewBox: false,
|
||||||
|
removeXMLNS: true,
|
||||||
|
removeXMLProcInst: true,
|
||||||
|
reusePaths: true,
|
||||||
|
sortAttrs: true,
|
||||||
|
sortDefsChildren: true,
|
||||||
|
convertColors: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
|
||||||
|
const tsConfigPath = resolve('tsconfig.json');
|
||||||
|
|
||||||
|
config.resolve.plugins.push(
|
||||||
|
new TsconfigPathsPlugin({
|
||||||
|
configFile: tsConfigPath,
|
||||||
|
extensions,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
config.resolve.symlinks = true;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
92
packages/expo/project.json
Normal file
92
packages/expo/project.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"sourceRoot": "packages/expo/src",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": [
|
||||||
|
"packages/expo/**/*.ts",
|
||||||
|
"packages/expo/**/*.spec.ts",
|
||||||
|
"packages/expo/**/*.spec.tsx",
|
||||||
|
"packages/expo/**/*.spec.js",
|
||||||
|
"packages/expo/**/*.spec.jsx",
|
||||||
|
"packages/expo/**/*.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"outputs": ["{options.outputFile}"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "packages/expo/jest.config.ts",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/packages/expo"]
|
||||||
|
},
|
||||||
|
"build-base": {
|
||||||
|
"executor": "@nrwl/js:tsc",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "build/packages/expo",
|
||||||
|
"tsConfig": "packages/expo/tsconfig.lib.json",
|
||||||
|
"packageJson": "packages/expo/package.json",
|
||||||
|
"main": "packages/expo/index.ts",
|
||||||
|
"updateBuildableProjectDepsInPackageJson": false,
|
||||||
|
"assets": [
|
||||||
|
"packages/expo/*.md",
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/!(*.ts)",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/*.d.ts",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/files/**",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/files/**/.gitkeep",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/files/**/.babelrc.js.template",
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "packages/expo",
|
||||||
|
"glob": "**/*.json",
|
||||||
|
"ignore": ["**/tsconfig*.json", "**/project.json"],
|
||||||
|
"output": "/"
|
||||||
|
},
|
||||||
|
"LICENSE"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"outputs": ["{options.outputPath}"]
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-commands",
|
||||||
|
"outputs": ["build/packages/expo"],
|
||||||
|
"options": {
|
||||||
|
"command": "node ./scripts/copy-readme.js expo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publish": {
|
||||||
|
"executor": "@nrwl/workspace:run-commands",
|
||||||
|
"options": {
|
||||||
|
"parallel": false,
|
||||||
|
"commands": [
|
||||||
|
"nx build expo",
|
||||||
|
"node tools/scripts/publish.mjs expo {args.ver} {args.tag}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoBuildAndroidOptions } from './schema';
|
||||||
|
import {
|
||||||
|
displayNewlyAddedDepsMessage,
|
||||||
|
syncDeps,
|
||||||
|
} from '../sync-deps/sync-deps.impl';
|
||||||
|
|
||||||
|
export interface ReactNativeBuildOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildAndroidExecutor(
|
||||||
|
options: ExpoBuildAndroidOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeBuildOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
if (options.sync) {
|
||||||
|
displayNewlyAddedDepsMessage(
|
||||||
|
context.projectName,
|
||||||
|
await syncDeps(context.projectName, projectRoot)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliBuild(context.root, projectRoot, options);
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliBuild(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoBuildAndroidOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['build:android', ...createRunOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['sync'];
|
||||||
|
|
||||||
|
function createRunOptions(options: ExpoBuildAndroidOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (!nxOptions.includes(k)) {
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build-android/compat.ts
Normal file
5
packages/expo/src/executors/build-android/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildAndroidExecutor from './build-android.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildAndroidExecutor);
|
||||||
13
packages/expo/src/executors/build-android/schema.d.ts
vendored
Normal file
13
packages/expo/src/executors/build-android/schema.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-buildandroid
|
||||||
|
export interface ExpoBuildAndroidOptions {
|
||||||
|
clearCredentials?: boolean;
|
||||||
|
type?: 'app-bundle' | 'apk';
|
||||||
|
releaseChannel?: string;
|
||||||
|
noPublish?: boolean;
|
||||||
|
noWait?: boolean;
|
||||||
|
keystorePath?: string;
|
||||||
|
keystoreAlias?: string;
|
||||||
|
publicUrl?: string;
|
||||||
|
skipWorkflowCheck?: boolean;
|
||||||
|
sync: boolean; // default is true
|
||||||
|
}
|
||||||
54
packages/expo/src/executors/build-android/schema.json
Normal file
54
packages/expo/src/executors/build-android/schema.json
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoBuildAndroid",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo Android Build executor",
|
||||||
|
"description": "Build and sign a standalone APK or App Bundle for the Google Play Store",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"clearCredentials": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear all credentials stored on Expo servers.",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": ["app-bundle", "apk"],
|
||||||
|
"description": "Type of build: [app-bundle⎮apk].",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"releaseChannel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Pull from specified release channel."
|
||||||
|
},
|
||||||
|
"noPublish": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disable automatic publishing before building."
|
||||||
|
},
|
||||||
|
"noWait": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Exit immediately after scheduling build."
|
||||||
|
},
|
||||||
|
"keystorePath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to your Keystore: *.jks."
|
||||||
|
},
|
||||||
|
"keystoreAlias": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Keystore Alias"
|
||||||
|
},
|
||||||
|
"publicUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The URL of an externally hosted manifest (for self-hosted apps)."
|
||||||
|
},
|
||||||
|
"skipWorkflowCheck": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip warning about build service bare workflow limitations."
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Syncs npm dependencies to package.json (for React Native autolink). Always true when --install is used.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
88
packages/expo/src/executors/build-ios/build-ios.impl.ts
Normal file
88
packages/expo/src/executors/build-ios/build-ios.impl.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import {
|
||||||
|
displayNewlyAddedDepsMessage,
|
||||||
|
syncDeps,
|
||||||
|
} from '../sync-deps/sync-deps.impl';
|
||||||
|
import { ExpoBuildIOSOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoRunOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildIosExecutor(
|
||||||
|
options: ExpoBuildIOSOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoRunOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
if (options.sync) {
|
||||||
|
displayNewlyAddedDepsMessage(
|
||||||
|
context.projectName,
|
||||||
|
await syncDeps(context.projectName, projectRoot)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliBuildIOS(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliBuildIOS(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoBuildIOSOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['build:ios', ...createRunOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['sync'];
|
||||||
|
|
||||||
|
function createRunOptions(options: ExpoBuildIOSOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (!nxOptions.includes(k)) {
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build-ios/compat.ts
Normal file
5
packages/expo/src/executors/build-ios/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildIosExecutor from './build-ios.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildIosExecutor);
|
||||||
23
packages/expo/src/executors/build-ios/schema.d.ts
vendored
Normal file
23
packages/expo/src/executors/build-ios/schema.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-buildios
|
||||||
|
export interface ExpoBuildIOSOptions {
|
||||||
|
clearCredentials?: boolean;
|
||||||
|
clearDistCert?: boolean;
|
||||||
|
clearPushKey?: boolean;
|
||||||
|
clearnPushCert?: boolean;
|
||||||
|
clearProvisioningProfile?: boolean;
|
||||||
|
revokeCredentials?: boolean;
|
||||||
|
appleId?: string;
|
||||||
|
type: 'archive' | 'simulator';
|
||||||
|
releaseChannel?: string;
|
||||||
|
noPublish?: boolean;
|
||||||
|
noWait?: boolean;
|
||||||
|
teamId?: string;
|
||||||
|
dishP12Path?: string;
|
||||||
|
pushId?: string;
|
||||||
|
pushP8Path?: string;
|
||||||
|
provisioningProfile?: string;
|
||||||
|
publicUrl?: string;
|
||||||
|
skipCredentialsCheck?: boolean;
|
||||||
|
skipWorkflowCheck?: boolean;
|
||||||
|
sync: boolean; // default is true
|
||||||
|
}
|
||||||
91
packages/expo/src/executors/build-ios/schema.json
Normal file
91
packages/expo/src/executors/build-ios/schema.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoBuildIOS",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo iOS Build executor",
|
||||||
|
"description": "Build and sign a standalone IPA for the Apple App Store",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"clearCredentials": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear all credentials stored on Expo servers.",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
"clearDistCert": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Remove Distribution Certificate stored on Expo servers."
|
||||||
|
},
|
||||||
|
"clearPushKey": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Remove Push Notifications Key stored on Expo servers."
|
||||||
|
},
|
||||||
|
"clearPushCert": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Remove Push Notifications Certificate stored on Expo servers. Use of Push Notifications Certificates is deprecated."
|
||||||
|
},
|
||||||
|
"clearProvisioningProfile": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Remove Provisioning Profile stored on Expo servers."
|
||||||
|
},
|
||||||
|
"revokeCredentials": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Revoke credentials on developer.apple.com, select appropriate using --clear-* options.",
|
||||||
|
"alias": "r"
|
||||||
|
},
|
||||||
|
"appleId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Apple ID username (please also set the Apple ID password as EXPO_APPLE_PASSWORD environment variable)."
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": ["archive", "simulator"],
|
||||||
|
"description": "Type of build: [archive⎮simulator].",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"releaseChannel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Pull from specified release channel."
|
||||||
|
},
|
||||||
|
"noPublish": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disable automatic publishing before building."
|
||||||
|
},
|
||||||
|
"noWait": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Exit immediately after scheduling build."
|
||||||
|
},
|
||||||
|
"teamId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Apple Team ID."
|
||||||
|
},
|
||||||
|
"distP12Path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to your Distribution Certificate P12 (set password as EXPO_IOS_DIST_P12_PASSWORD environment variable)."
|
||||||
|
},
|
||||||
|
"pushP8Path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to your Push Key .p8 file."
|
||||||
|
},
|
||||||
|
"provisioningProfilePath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to your Provisioning Profile."
|
||||||
|
},
|
||||||
|
"publicUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The URL of an externally hosted manifest (for self-hosted apps)."
|
||||||
|
},
|
||||||
|
"skipCredentialsCheck": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip checking credentials."
|
||||||
|
},
|
||||||
|
"skipWorkflowCheck": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip warning about build service bare workflow limitations."
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Syncs npm dependencies to package.json (for React Native autolink). Always true when --install is used.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
53
packages/expo/src/executors/build-list/build-list.impl.ts
Normal file
53
packages/expo/src/executors/build-list/build-list.impl.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { ExecutorContext, logger, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoEasBuildListOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ReactNativeBuildListOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* buildListExecutor(
|
||||||
|
options: ExpoEasBuildListOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeBuildListOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
logger.info(runCliBuildList(context.root, projectRoot, options));
|
||||||
|
yield { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runCliBuildList(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoEasBuildListOptions
|
||||||
|
): string {
|
||||||
|
return execSync(
|
||||||
|
`./node_modules/eas-cli/bin/run build:list ${createBuildListOptions(
|
||||||
|
options
|
||||||
|
).join(' ')}`,
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['output'];
|
||||||
|
function createBuildListOptions(options: ExpoEasBuildListOptions): string[] {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (!nxOptions.includes(k)) {
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in camel case
|
||||||
|
acc.push(`--${names(k).propertyName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).propertyName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build-list/compat.ts
Normal file
5
packages/expo/src/executors/build-list/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildListExecutor from './build-list.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildListExecutor);
|
||||||
24
packages/expo/src/executors/build-list/schema.d.ts
vendored
Normal file
24
packages/expo/src/executors/build-list/schema.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// command to run https://github.com/expo/eas-cli/tree/main#eas-buildlist
|
||||||
|
// options from https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/commands/build/list.ts
|
||||||
|
export interface ExpoEasBuildListOptions {
|
||||||
|
platform: 'ios' | 'android' | 'all';
|
||||||
|
json?: boolean;
|
||||||
|
// status and distribution enum from https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/build/types.ts
|
||||||
|
status?:
|
||||||
|
| 'new'
|
||||||
|
| 'in-queue'
|
||||||
|
| 'in-progress'
|
||||||
|
| 'errored'
|
||||||
|
| 'finished'
|
||||||
|
| 'canceled';
|
||||||
|
distribution?: 'store' | 'internal' | 'simulator';
|
||||||
|
channel?: string;
|
||||||
|
appVersion?: string;
|
||||||
|
appBuildVersion?: string;
|
||||||
|
sdkVersion?: string;
|
||||||
|
runtimeVersion?: string;
|
||||||
|
appIdentifier?: string;
|
||||||
|
buildProject?: string;
|
||||||
|
gitCommitHash?: string;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
71
packages/expo/src/executors/build-list/schema.json
Normal file
71
packages/expo/src/executors/build-list/schema.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoEasBuildList",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo EAS Build List executor",
|
||||||
|
"description": "List all EAS builds for your Expo project",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"platform": {
|
||||||
|
"enum": ["ios", "android", "all"],
|
||||||
|
"alias": "p",
|
||||||
|
"description": "The platform to build the app, example values: ios, android, all."
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable JSON output, non-JSON messages will be printed to stderr"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"enum": [
|
||||||
|
"new",
|
||||||
|
"in-queue",
|
||||||
|
"in-progress",
|
||||||
|
"errored",
|
||||||
|
"finished",
|
||||||
|
"canceled"
|
||||||
|
],
|
||||||
|
"description": "Status of EAS build"
|
||||||
|
},
|
||||||
|
"distribution": {
|
||||||
|
"enum": ["store", "internal", "simulator"],
|
||||||
|
"description": "Distribution of EAS build"
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Channel of EAS build"
|
||||||
|
},
|
||||||
|
"appVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App version of EAS build"
|
||||||
|
},
|
||||||
|
"appBuildVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App build version of EAS build"
|
||||||
|
},
|
||||||
|
"sdkVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SDK version of EAS build"
|
||||||
|
},
|
||||||
|
"runtimeVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Runtime version of EAS build"
|
||||||
|
},
|
||||||
|
"appIdentifier": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App identifier of EAS build"
|
||||||
|
},
|
||||||
|
"buildProfile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Build profile of EAS build"
|
||||||
|
},
|
||||||
|
"gitCommitHash": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Git commit hash of EAS build"
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Limit of numbers to list EAS builds"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoBuildStatusOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ReactNativeBuildOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildStatusExecutor(
|
||||||
|
options: ExpoBuildStatusOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeBuildOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliBuild(context.root, projectRoot, options);
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliBuild(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoBuildStatusOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['build:status', ...createRunOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRunOptions(options) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build-status/compat.ts
Normal file
5
packages/expo/src/executors/build-status/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildStatusExecutor from './build-status.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildStatusExecutor);
|
||||||
4
packages/expo/src/executors/build-status/schema.d.ts
vendored
Normal file
4
packages/expo/src/executors/build-status/schema.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-buildweb
|
||||||
|
export interface ExpoBuildStatusOptions {
|
||||||
|
publicUrl: string;
|
||||||
|
}
|
||||||
15
packages/expo/src/executors/build-status/schema.json
Normal file
15
packages/expo/src/executors/build-status/schema.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoBuildStatus",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo web Build executor",
|
||||||
|
"description": "Get the status of the latest build for the project",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"publicUrl": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The URL of an externally hosted manifest (for self-hosted apps)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
74
packages/expo/src/executors/build-web/build-web.impl.ts
Normal file
74
packages/expo/src/executors/build-web/build-web.impl.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoBuildWebOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ReactNativeBuildOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildWebExecutor(
|
||||||
|
options: ExpoBuildWebOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeBuildOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliBuild(context.root, projectRoot, options);
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliBuild(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoBuildWebOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['build:web', ...createRunOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRunOptions(options) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build-web/compat.ts
Normal file
5
packages/expo/src/executors/build-web/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildWebExecutor from './build-web.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildWebExecutor);
|
||||||
6
packages/expo/src/executors/build-web/schema.d.ts
vendored
Normal file
6
packages/expo/src/executors/build-web/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-buildweb
|
||||||
|
export interface ExpoBuildWebOptions {
|
||||||
|
clear?: boolean;
|
||||||
|
noPwa?: boolean;
|
||||||
|
dev?: boolean;
|
||||||
|
}
|
||||||
24
packages/expo/src/executors/build-web/schema.json
Normal file
24
packages/expo/src/executors/build-web/schema.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoBuildWeb",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo web Build executor",
|
||||||
|
"description": "Build the web app for production",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"clear": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear all cached build files and assets.",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
"noPwa": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Prevent webpack from generating the manifest.json and injecting meta into the index.html head."
|
||||||
|
},
|
||||||
|
"dev": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Turns dev flag on before bundling"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
77
packages/expo/src/executors/build/build.impl.ts
Normal file
77
packages/expo/src/executors/build/build.impl.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoEasBuildOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ReactNativeBuildOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildExecutor(
|
||||||
|
options: ExpoEasBuildOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeBuildOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliBuild(context.root, projectRoot, options);
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliBuild(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoEasBuildOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
|
||||||
|
['build', ...createBuildOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBuildOptions(options: ExpoEasBuildOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
if (v === false && k === 'wait') {
|
||||||
|
acc.push('--no-wait');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/build/compat.ts
Normal file
5
packages/expo/src/executors/build/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildExecutor from './build.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildExecutor);
|
||||||
14
packages/expo/src/executors/build/schema.d.ts
vendored
Normal file
14
packages/expo/src/executors/build/schema.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// command to run https://github.com/expo/eas-cli/tree/main#eas-build
|
||||||
|
// options from github.com/expo/eas-cli/blob/main/packages/eas-cli/src/commands/build/index.ts
|
||||||
|
export interface ExpoEasBuildOptions {
|
||||||
|
platform: 'ios' | 'android' | 'all';
|
||||||
|
profile?: string;
|
||||||
|
nonInteractive: boolean; // default is false
|
||||||
|
local: boolean; // default is false
|
||||||
|
output?: string;
|
||||||
|
wait: boolean; // default is true
|
||||||
|
clearCache: boolean; // default is false
|
||||||
|
json: boolean; // default is false
|
||||||
|
autoSubmit: boolean; // default is false
|
||||||
|
autoSubmitWithProfile?: string;
|
||||||
|
}
|
||||||
56
packages/expo/src/executors/build/schema.json
Normal file
56
packages/expo/src/executors/build/schema.json
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoEasBuild",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo EAS Build executor",
|
||||||
|
"description": "Start an EAS build for your expo project",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"platform": {
|
||||||
|
"enum": ["ios", "android", "all"],
|
||||||
|
"alias": "p",
|
||||||
|
"description": "The platform to build the app, example values: ios, android, all."
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable JSON output, non-JSON messages will be printed to stderr",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the build profile from eas.json. Defaults to \"production\" if defined in eas.json.",
|
||||||
|
"examples": ["PROFILE_NAME"]
|
||||||
|
},
|
||||||
|
"nonInteractive": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Run command in non-interactive mode",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Run build locally [experimental]",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"wait": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Wait for build(s) to complete",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"clearCache": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear cache before the build",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"autoSubmit": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Submit on build complete using the submit profile with the same name as the build profile",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"autoSubmitWithProfile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Submit on build complete using the submit profile with provided name",
|
||||||
|
"examples": ["PROFILE_NAME"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/download/compat.ts
Normal file
5
packages/expo/src/executors/download/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import downloadExecutor from './download.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(downloadExecutor);
|
||||||
137
packages/expo/src/executors/download/download.impl.ts
Normal file
137
packages/expo/src/executors/download/download.impl.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { ExecutorContext, logger, names } from '@nrwl/devkit';
|
||||||
|
import {
|
||||||
|
copyFile,
|
||||||
|
createReadStream,
|
||||||
|
createWriteStream,
|
||||||
|
existsSync,
|
||||||
|
mkdirSync,
|
||||||
|
} from 'fs';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
import { pipeline } from 'stream';
|
||||||
|
import * as chalk from 'chalk';
|
||||||
|
import { join } from 'path';
|
||||||
|
import * as tar from 'tar-fs';
|
||||||
|
import { createUnzip } from 'zlib';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoEasDownloadOptions } from './schema';
|
||||||
|
import { runCliBuildList } from '../build-list/build-list.impl';
|
||||||
|
|
||||||
|
export interface ReactNativeDownloadOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const streamPipeline = promisify(pipeline);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This executor downloads the latest EAS build.
|
||||||
|
* It calls the build list exectuor to list EAS builds with options passed in.
|
||||||
|
*/
|
||||||
|
export default async function* downloadExecutor(
|
||||||
|
options: ExpoEasDownloadOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeDownloadOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
const build = getBuild(context.root, projectRoot, options);
|
||||||
|
const buildUrl = build?.artifacts?.buildUrl;
|
||||||
|
if (!buildUrl) {
|
||||||
|
throw new Error(`No build URL found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existsSync(options.output)) {
|
||||||
|
mkdirSync(options.output, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadFileName = buildUrl.split('/').pop();
|
||||||
|
const downloadFilePath = join(options.output, downloadFileName);
|
||||||
|
await downloadBuild(buildUrl, downloadFilePath);
|
||||||
|
|
||||||
|
const appExtension = getAppExtension(build.platform, downloadFileName);
|
||||||
|
const appName = `${names(build.project?.name).className}${appExtension}`;
|
||||||
|
const outputFilePath = join(options.output, appName);
|
||||||
|
|
||||||
|
if (downloadFileName.endsWith('.tar.gz')) {
|
||||||
|
await unzipBuild(downloadFilePath, options.output);
|
||||||
|
} else {
|
||||||
|
await copyBuildFile(downloadFilePath, outputFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Succesfully download the build to ${outputFilePath}`);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadBuild(buildUrl: string, output: string) {
|
||||||
|
const response = await fetch(buildUrl);
|
||||||
|
|
||||||
|
if (!response.ok)
|
||||||
|
throw new Error(
|
||||||
|
`Unable to download the build ${buildUrl}. Error: ${response.statusText}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return streamPipeline(response.body, createWriteStream(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBuild(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoEasDownloadOptions
|
||||||
|
) {
|
||||||
|
const buildList = runCliBuildList(workspaceRoot, projectRoot, {
|
||||||
|
...options,
|
||||||
|
json: true,
|
||||||
|
status: 'finished',
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
const builds = JSON.parse(buildList);
|
||||||
|
if (!builds.length) {
|
||||||
|
throw new Error(
|
||||||
|
`No EAS build found. Please check expo.dev to make sure your build is finished.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logger.info(`${chalk.bold.cyan('info')} Found build: ${buildList}`);
|
||||||
|
|
||||||
|
return builds[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAppExtension(
|
||||||
|
platform: string,
|
||||||
|
downloadFileName: string
|
||||||
|
): string {
|
||||||
|
platform = platform.toLowerCase();
|
||||||
|
if (platform === 'ios') {
|
||||||
|
return '.app';
|
||||||
|
}
|
||||||
|
if (downloadFileName.includes('.')) {
|
||||||
|
return `.${downloadFileName.split('.').pop()}`;
|
||||||
|
}
|
||||||
|
throw new Error(`Invalid build name found: ${downloadFileName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unzipBuild(
|
||||||
|
downloadFilePath: string,
|
||||||
|
outputDirectoryPath: string
|
||||||
|
) {
|
||||||
|
const unzip = createUnzip();
|
||||||
|
const extract = tar.extract(outputDirectoryPath);
|
||||||
|
return streamPipeline(createReadStream(downloadFilePath), unzip, extract);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copyBuildFile(
|
||||||
|
downloadFilePath: string,
|
||||||
|
outputFilePath: string
|
||||||
|
) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
copyFile(downloadFilePath, outputFilePath, (error) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
15
packages/expo/src/executors/download/schema.d.ts
vendored
Normal file
15
packages/expo/src/executors/download/schema.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// subset options from https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/commands/build/list.ts
|
||||||
|
export interface ExpoEasDownloadOptions {
|
||||||
|
platform: 'ios' | 'android' | 'all';
|
||||||
|
// status and distribution enum from https://github.com/expo/eas-cli/blob/main/packages/eas-cli/src/build/types.ts
|
||||||
|
distribution?: 'store' | 'internal' | 'simulator';
|
||||||
|
channel?: string;
|
||||||
|
appVersion?: string;
|
||||||
|
appBuildVersion?: string;
|
||||||
|
sdkVersion?: string;
|
||||||
|
runtimeVersion?: string;
|
||||||
|
appIdentifier?: string;
|
||||||
|
buildProject?: string;
|
||||||
|
gitCommitHash?: string;
|
||||||
|
output: string;
|
||||||
|
}
|
||||||
56
packages/expo/src/executors/download/schema.json
Normal file
56
packages/expo/src/executors/download/schema.json
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoDownloadEasBuild",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Download EAS Build executor",
|
||||||
|
"description": "Download an EAS build",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"platform": {
|
||||||
|
"enum": ["ios", "android"],
|
||||||
|
"alias": "p",
|
||||||
|
"description": "The platform to build the app, example values: ios, android, all."
|
||||||
|
},
|
||||||
|
"distribution": {
|
||||||
|
"enum": ["store", "internal", "simulator"],
|
||||||
|
"description": "Distribution of EAS build"
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Channel of EAS build"
|
||||||
|
},
|
||||||
|
"appVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App version of EAS build"
|
||||||
|
},
|
||||||
|
"appBuildVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App build version of EAS build"
|
||||||
|
},
|
||||||
|
"sdkVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SDK version of EAS build"
|
||||||
|
},
|
||||||
|
"runtimeVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Runtime version of EAS build"
|
||||||
|
},
|
||||||
|
"appIdentifier": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "App identifier of EAS build"
|
||||||
|
},
|
||||||
|
"buildProfile": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Build profile of EAS build"
|
||||||
|
},
|
||||||
|
"gitCommitHash": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Git commit hash of EAS build"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Output directory for the download build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["output"]
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/eject/compat.ts
Normal file
5
packages/expo/src/executors/eject/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import ejectExecutor from './eject.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(ejectExecutor);
|
||||||
77
packages/expo/src/executors/eject/eject.impl.ts
Normal file
77
packages/expo/src/executors/eject/eject.impl.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { ExecutorContext } from '@nrwl/devkit';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import { podInstall } from '../../utils/pod-install-task';
|
||||||
|
import { ExpoEjectOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoEjectOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* ejectExecutor(
|
||||||
|
options: ExpoEjectOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoEjectOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ejectAsync(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
if (options.install) {
|
||||||
|
await podInstall(join(context.root, projectRoot, 'ios'));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ejectAsync(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoEjectOptions
|
||||||
|
): Promise<number> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['eject', ...createEjectOptions(options), '--no-install'],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['install'];
|
||||||
|
function createEjectOptions(options: ExpoEjectOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (!nxOptions.includes(k)) {
|
||||||
|
acc.push(`--${k}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
6
packages/expo/src/executors/eject/schema.d.ts
vendored
Normal file
6
packages/expo/src/executors/eject/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#eject
|
||||||
|
|
||||||
|
export interface ExpoEjectOptions {
|
||||||
|
install: boolean; // default is true
|
||||||
|
platform: 'all' | 'android' | 'ios'; // default is all
|
||||||
|
}
|
||||||
21
packages/expo/src/executors/eject/schema.json
Normal file
21
packages/expo/src/executors/eject/schema.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoEject",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Expo Eject",
|
||||||
|
"description": "Create native iOS and Android project files",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"install": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Install CocoaPods.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Platforms to sync",
|
||||||
|
"default": "all",
|
||||||
|
"examples": ["ios", "android", "all"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/ensure-symlink/compat.ts
Normal file
5
packages/expo/src/executors/ensure-symlink/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import ensureSymlinkExecutor from './ensure-symlink.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(ensureSymlinkExecutor);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { ExecutorContext } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
export interface ExpoEnsureSymlinkOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* ensureSymlinkExecutor(
|
||||||
|
_,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoEnsureSymlinkOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
}
|
||||||
9
packages/expo/src/executors/ensure-symlink/schema.json
Normal file
9
packages/expo/src/executors/ensure-symlink/schema.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoEnsureSymlink",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Ensure Symlink for Expo",
|
||||||
|
"description": "Ensure workspace node_modules is symlink under app's node_modules folder.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/publish-set/compat.ts
Normal file
5
packages/expo/src/executors/publish-set/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import publishSetExecutor from './publish-set.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(publishSetExecutor);
|
||||||
76
packages/expo/src/executors/publish-set/publish-set.impl.ts
Normal file
76
packages/expo/src/executors/publish-set/publish-set.impl.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import { ExpoPublishSetOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoPublishSetOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* publishSetExecutor(
|
||||||
|
options: ExpoPublishSetOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoPublishSetOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliPublishSet(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliPublishSet(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoPublishSetOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['publish:set', ...createPublishSetOptions(options)],
|
||||||
|
{
|
||||||
|
cwd: projectRoot,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPublishSetOptions(options: ExpoPublishSetOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
6
packages/expo/src/executors/publish-set/schema.d.ts
vendored
Normal file
6
packages/expo/src/executors/publish-set/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-publishrollback
|
||||||
|
export interface ExpoPublishSetOptions {
|
||||||
|
releaseChannel: string;
|
||||||
|
sdkVersion: string;
|
||||||
|
platform?: 'ios' | 'android';
|
||||||
|
}
|
||||||
19
packages/expo/src/executors/publish-set/schema.json
Normal file
19
packages/expo/src/executors/publish-set/schema.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoPublishSet",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Set Publish Channel for Expo",
|
||||||
|
"description": "Specify the channel to serve a published release",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"releaseChannel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The release channel to publish to."
|
||||||
|
},
|
||||||
|
"publishId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The id of the published release to serve from the channel."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["releaseChannel", "publishId"]
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/publish/compat.ts
Normal file
5
packages/expo/src/executors/publish/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import publishExecutor from './publish.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(publishExecutor);
|
||||||
94
packages/expo/src/executors/publish/publish.impl.ts
Normal file
94
packages/expo/src/executors/publish/publish.impl.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import {
|
||||||
|
displayNewlyAddedDepsMessage,
|
||||||
|
syncDeps,
|
||||||
|
} from '../sync-deps/sync-deps.impl';
|
||||||
|
import { ExpoPublishOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoPublishOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* publishExecutor(
|
||||||
|
options: ExpoPublishOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoPublishOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
if (options.sync) {
|
||||||
|
displayNewlyAddedDepsMessage(
|
||||||
|
context.projectName,
|
||||||
|
await syncDeps(context.projectName, projectRoot)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliPublish(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliPublish(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoPublishOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
[
|
||||||
|
'publish',
|
||||||
|
join(workspaceRoot, projectRoot),
|
||||||
|
...createPublishOptions(options),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: projectRoot,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['sync'];
|
||||||
|
|
||||||
|
function createPublishOptions(options: ExpoPublishOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (!nxOptions.includes(k)) {
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
10
packages/expo/src/executors/publish/schema.d.ts
vendored
Normal file
10
packages/expo/src/executors/publish/schema.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-publish
|
||||||
|
export interface ExpoPublishOptions {
|
||||||
|
quiet: boolean; // default is false
|
||||||
|
sendTo?: string;
|
||||||
|
clear: boolean; // default is false
|
||||||
|
target: 'managed' | 'bare';
|
||||||
|
maxWorkers?: number;
|
||||||
|
releaseChannel: string; // default is 'default'
|
||||||
|
sync: boolean; // default is true
|
||||||
|
}
|
||||||
42
packages/expo/src/executors/publish/schema.json
Normal file
42
packages/expo/src/executors/publish/schema.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoPublish",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Publish for Expo",
|
||||||
|
"description": "Deploy a project to Expo hosting",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"quiet": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Suppress verbose output from the Metro bundler",
|
||||||
|
"default": false,
|
||||||
|
"alias": "q"
|
||||||
|
},
|
||||||
|
"sendTo": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A phone number or email address to send a link to",
|
||||||
|
"alias": "s"
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear the Metro bundler cache",
|
||||||
|
"default": false,
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"enum": ["managed", "bare"],
|
||||||
|
"default": "managed",
|
||||||
|
"description": "Target environment for which this publish is intended. Options are managed or bare.",
|
||||||
|
"alias": "t"
|
||||||
|
},
|
||||||
|
"maxWorkers": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||||
|
},
|
||||||
|
"releaseChannel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The release channel to publish to. Default is 'default'.",
|
||||||
|
"default": "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/rollback/compat.ts
Normal file
5
packages/expo/src/executors/rollback/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import rollbackExecutor from './rollback.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(rollbackExecutor);
|
||||||
76
packages/expo/src/executors/rollback/rollback.impl.ts
Normal file
76
packages/expo/src/executors/rollback/rollback.impl.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import { ExpoRollbackOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoRollbackOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* rollbackExecutor(
|
||||||
|
options: ExpoRollbackOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoRollbackOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliRollback(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliRollback(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoRollbackOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['publish:rollback', ...createRollbackOptions(options)],
|
||||||
|
{
|
||||||
|
cwd: projectRoot,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRollbackOptions(options: ExpoRollbackOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
6
packages/expo/src/executors/rollback/schema.d.ts
vendored
Normal file
6
packages/expo/src/executors/rollback/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-publishrollback
|
||||||
|
export interface ExpoRollbackOptions {
|
||||||
|
releaseChannel: string;
|
||||||
|
sdkVersion: string;
|
||||||
|
platform?: 'ios' | 'android';
|
||||||
|
}
|
||||||
23
packages/expo/src/executors/rollback/schema.json
Normal file
23
packages/expo/src/executors/rollback/schema.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoRollback",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Rollback Publish Command for Expo",
|
||||||
|
"description": "Undo an update to a channel",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"releaseChannel": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The release channel to publish to."
|
||||||
|
},
|
||||||
|
"sdkVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The sdk version to rollback."
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"enum": ["ios", "android"],
|
||||||
|
"description": "The platform to rollback."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["releaseChannel", "sdkVersion"]
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/run/compat.ts
Normal file
5
packages/expo/src/executors/run/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import runExecutor from './run.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(runExecutor);
|
||||||
109
packages/expo/src/executors/run/run.impl.ts
Normal file
109
packages/expo/src/executors/run/run.impl.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
import { platform } from 'os';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import {
|
||||||
|
displayNewlyAddedDepsMessage,
|
||||||
|
syncDeps,
|
||||||
|
} from '../sync-deps/sync-deps.impl';
|
||||||
|
import { ExpoRunOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoRunOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* runExecutor(
|
||||||
|
options: ExpoRunOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoRunOutput> {
|
||||||
|
if (platform() !== 'darwin' && options.platform === 'ios') {
|
||||||
|
throw new Error(`The run-ios build requires Mac to run`);
|
||||||
|
}
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
if (options.sync) {
|
||||||
|
displayNewlyAddedDepsMessage(
|
||||||
|
context.projectName,
|
||||||
|
await syncDeps(context.projectName, projectRoot)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliRun(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliRun(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoRunOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
['run:' + options.platform, ...createRunOptions(options)],
|
||||||
|
{
|
||||||
|
cwd: projectRoot,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['sync', 'platform'];
|
||||||
|
const iOSOptions = ['xcodeConfiguration', 'schema'];
|
||||||
|
const androidOptions = ['variant'];
|
||||||
|
|
||||||
|
function createRunOptions(options: ExpoRunOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
if (
|
||||||
|
nxOptions.includes(k) ||
|
||||||
|
(options.platform === 'ios' && androidOptions.includes(k)) ||
|
||||||
|
(options.platform === 'android' && iOSOptions.includes(k))
|
||||||
|
) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
const v = options[k];
|
||||||
|
{
|
||||||
|
if (k === 'xcodeConfiguration') {
|
||||||
|
acc.push('--configuration', v);
|
||||||
|
} else if (k === 'bundler') {
|
||||||
|
if (v === false) {
|
||||||
|
acc.push('--no-bundler');
|
||||||
|
}
|
||||||
|
} else if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
11
packages/expo/src/executors/run/schema.d.ts
vendored
Normal file
11
packages/expo/src/executors/run/schema.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-runios and https://docs.expo.dev/workflow/expo-cli/#expo-runandroid
|
||||||
|
export interface ExpoRunOptions {
|
||||||
|
platform: 'ios' | 'android';
|
||||||
|
xcodeConfiguration: string; // iOS only, default is Debug
|
||||||
|
scheme?: string; // iOS only
|
||||||
|
variant: string; // android only, default is debug
|
||||||
|
port: number; // default is 8081
|
||||||
|
bundler: boolean; // default is true
|
||||||
|
sync: boolean; // default is true
|
||||||
|
device?: string;
|
||||||
|
}
|
||||||
51
packages/expo/src/executors/run/schema.json
Normal file
51
packages/expo/src/executors/run/schema.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoRun",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Run iOS or Android application",
|
||||||
|
"description": "Run Expo target options",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"platform": {
|
||||||
|
"description": "Platform to run for (ios, android).",
|
||||||
|
"enum": ["ios", "android"],
|
||||||
|
"default": "ios"
|
||||||
|
},
|
||||||
|
"xcodeConfiguration": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "(iOS) Xcode configuration to use. Debug or Release",
|
||||||
|
"default": "Debug"
|
||||||
|
},
|
||||||
|
"scheme": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "(iOS) Explicitly set the Xcode scheme to use"
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "(Android) Specify your app's build variant (e.g. debug, release).",
|
||||||
|
"default": "debug"
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Device name or UDID to build the app on. The value is not required if you have a single device connected.",
|
||||||
|
"alias": "d"
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Syncs npm dependencies to package.json (for React Native autolink). Always true when --install is used.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Port to start the Metro bundler on",
|
||||||
|
"default": 8081,
|
||||||
|
"alias": "p"
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to skip starting the Metro bundler. True to start it, false to skip it.",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["platform"]
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/start/compat.ts
Normal file
5
packages/expo/src/executors/start/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import startExecutor from './start.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(startExecutor);
|
||||||
22
packages/expo/src/executors/start/schema.d.ts
vendored
Normal file
22
packages/expo/src/executors/start/schema.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// options from https://docs.expo.dev/workflow/expo-cli/#expo-start
|
||||||
|
|
||||||
|
export interface ExpoStartOptions {
|
||||||
|
port: number;
|
||||||
|
dev?: boolean;
|
||||||
|
devClient?: boolean;
|
||||||
|
minify?: boolean;
|
||||||
|
https?: boolean;
|
||||||
|
clear?: boolean;
|
||||||
|
maxWorkers?: number;
|
||||||
|
scheme?: string;
|
||||||
|
sendTo?: string;
|
||||||
|
ios?: boolean;
|
||||||
|
android?: boolean;
|
||||||
|
web?: boolean;
|
||||||
|
host?: string;
|
||||||
|
lan?: boolean;
|
||||||
|
localhost?: boolean;
|
||||||
|
tunnel?: boolean;
|
||||||
|
offline?: boolean;
|
||||||
|
webpack?: boolean;
|
||||||
|
}
|
||||||
85
packages/expo/src/executors/start/schema.json
Normal file
85
packages/expo/src/executors/start/schema.json
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoStart",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Packager Server for Expo",
|
||||||
|
"description": "Packager Server target options",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"port": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Port to start the native Metro bundler on (does not apply to web or tunnel)",
|
||||||
|
"default": 19000,
|
||||||
|
"alias": "p"
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Clear the Metro bundler cache",
|
||||||
|
"alias": "c"
|
||||||
|
},
|
||||||
|
"maxWorkers": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Maximum number of tasks to allow Metro to spawn"
|
||||||
|
},
|
||||||
|
"dev": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Turn development mode on or off"
|
||||||
|
},
|
||||||
|
"devClient": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Experimental: Starts the bundler for use with the expo-development-client"
|
||||||
|
},
|
||||||
|
"minify": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether or not to minify code"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "To start webpack with https or http protocol"
|
||||||
|
},
|
||||||
|
"scheme": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Custom URI protocol to use with a development build"
|
||||||
|
},
|
||||||
|
"sentTo": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "An email address to send a link to",
|
||||||
|
"alias": "s"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Opens your app in Expo Go on a connected Android device",
|
||||||
|
"alias": "a"
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Opens your app in Expo Go in a currently running iOS simulator on your computer",
|
||||||
|
"alias": "i"
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "lan (default), tunnel, localhost. Type of host to use. \"tunnel\" allows you to view your link on other networks",
|
||||||
|
"alias": "m"
|
||||||
|
},
|
||||||
|
"tunnel": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Same as --host tunnel"
|
||||||
|
},
|
||||||
|
"lan": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Same as --host lan"
|
||||||
|
},
|
||||||
|
"localhost": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Same as --host localhost"
|
||||||
|
},
|
||||||
|
"offline": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Allows this command to run while offline"
|
||||||
|
},
|
||||||
|
"webpack": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Start a Webpack dev server for the web app."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
packages/expo/src/executors/start/start.impl.ts
Normal file
91
packages/expo/src/executors/start/start.impl.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import * as chalk from 'chalk';
|
||||||
|
import { ExecutorContext, logger, names } from '@nrwl/devkit';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
import { ExpoStartOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoStartOutput {
|
||||||
|
baseUrl?: string;
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* startExecutor(
|
||||||
|
options: ExpoStartOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoStartOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const baseUrl = `http://localhost:${options.port}`;
|
||||||
|
logger.info(chalk.cyan(`Packager is ready at ${baseUrl}`));
|
||||||
|
|
||||||
|
await startAsync(context.root, projectRoot, options);
|
||||||
|
|
||||||
|
yield {
|
||||||
|
baseUrl,
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAsync(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoStartOptions
|
||||||
|
): Promise<number> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/expo-cli/bin/expo.js'),
|
||||||
|
[options.webpack ? 'web' : 'start', ...createStartOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const nxOptions = ['webpack'];
|
||||||
|
function createStartOptions(options: ExpoStartOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (k === 'dev' && v === false) {
|
||||||
|
acc.push(`--no-dev`);
|
||||||
|
} else if (k === 'minify' && v === false) {
|
||||||
|
acc.push(`--no-minify`);
|
||||||
|
} else if (k === 'https' && v === false) {
|
||||||
|
acc.push(`--no-https`);
|
||||||
|
} else if (!nxOptions.includes(k)) {
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/sync-deps/compat.ts
Normal file
5
packages/expo/src/executors/sync-deps/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import syncDepsExecutor from './sync-deps.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(syncDepsExecutor);
|
||||||
3
packages/expo/src/executors/sync-deps/schema.d.ts
vendored
Normal file
3
packages/expo/src/executors/sync-deps/schema.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface ExpoSyncDepsOptions {
|
||||||
|
include: string;
|
||||||
|
}
|
||||||
14
packages/expo/src/executors/sync-deps/schema.json
Normal file
14
packages/expo/src/executors/sync-deps/schema.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"$id": "NxExpoSyncDeps",
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"title": "Sync Deps for Expo",
|
||||||
|
"description": "Updates package.json with project dependencies",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"include": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A comma-separated list of additional npm packages to include. e.g. 'nx sync-deps --include=react-native-gesture-handler,react-native-safe-area-context'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
packages/expo/src/executors/sync-deps/sync-deps.impl.ts
Normal file
85
packages/expo/src/executors/sync-deps/sync-deps.impl.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import * as chalk from 'chalk';
|
||||||
|
import {
|
||||||
|
ExecutorContext,
|
||||||
|
logger,
|
||||||
|
readJsonFile,
|
||||||
|
writeJsonFile,
|
||||||
|
createProjectGraphAsync,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { findAllNpmDependencies } from '../../utils/find-all-npm-dependencies';
|
||||||
|
|
||||||
|
import { ExpoSyncDepsOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ExpoSyncDepsOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function* syncDepsExecutor(
|
||||||
|
options: ExpoSyncDepsOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ExpoSyncDepsOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
displayNewlyAddedDepsMessage(
|
||||||
|
context.projectName,
|
||||||
|
await syncDeps(context.projectName, projectRoot, options.include)
|
||||||
|
);
|
||||||
|
|
||||||
|
yield { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncDeps(
|
||||||
|
projectName: string,
|
||||||
|
projectRoot: string,
|
||||||
|
include?: string
|
||||||
|
): Promise<string[]> {
|
||||||
|
const graph = await createProjectGraphAsync();
|
||||||
|
const npmDeps = findAllNpmDependencies(graph, projectName);
|
||||||
|
const packageJsonPath = join(projectRoot, 'package.json');
|
||||||
|
const packageJson = readJsonFile(packageJsonPath);
|
||||||
|
const newDeps = [];
|
||||||
|
const includeDeps = include?.split(',');
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
|
if (!packageJson.dependencies) {
|
||||||
|
packageJson.dependencies = {};
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDeps) {
|
||||||
|
npmDeps.push(...includeDeps);
|
||||||
|
}
|
||||||
|
|
||||||
|
npmDeps.forEach((dep) => {
|
||||||
|
if (!packageJson.dependencies[dep]) {
|
||||||
|
packageJson.dependencies[dep] = '*';
|
||||||
|
newDeps.push(dep);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
writeJsonFile(packageJsonPath, packageJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDeps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function displayNewlyAddedDepsMessage(
|
||||||
|
projectName: string,
|
||||||
|
deps: string[]
|
||||||
|
) {
|
||||||
|
if (deps.length > 0) {
|
||||||
|
logger.info(`${chalk.bold.cyan(
|
||||||
|
'info'
|
||||||
|
)} Added entries to 'package.json' for '${projectName}' (for autolink):
|
||||||
|
${deps.map((d) => chalk.bold.cyan(`"${d}": "*"`)).join('\n ')}`);
|
||||||
|
} else {
|
||||||
|
logger.info(
|
||||||
|
`${chalk.bold.cyan(
|
||||||
|
'info'
|
||||||
|
)} Dependencies for '${projectName}' are up to date! No changes made.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/expo/src/executors/update/compat.ts
Normal file
5
packages/expo/src/executors/update/compat.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { convertNxExecutor } from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import buildExecutor from './update.impl';
|
||||||
|
|
||||||
|
export default convertNxExecutor(buildExecutor);
|
||||||
15
packages/expo/src/executors/update/schema.d.ts
vendored
Normal file
15
packages/expo/src/executors/update/schema.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// command to run https://github.com/expo/eas-cli/tree/main#eas-update
|
||||||
|
// options from github.com/expo/eas-cli/blob/main/packages/eas-cli/src/commands/update/index.ts
|
||||||
|
export interface ExpoEasUpdateOptions {
|
||||||
|
branch?: string;
|
||||||
|
message?: string;
|
||||||
|
republish: boolean; // default is false
|
||||||
|
group?: string;
|
||||||
|
inputDir: string; // default is "dist"
|
||||||
|
skipBundler: boolean; // default is false
|
||||||
|
platform: 'ios' | 'android' | 'all'; // default is "all"
|
||||||
|
json: boolean; // default is false
|
||||||
|
auto: boolean; // default is false
|
||||||
|
privateKeyPath?: string;
|
||||||
|
nonInteractive: boolean; // default is false
|
||||||
|
}
|
||||||
62
packages/expo/src/executors/update/schema.json
Normal file
62
packages/expo/src/executors/update/schema.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/schema",
|
||||||
|
"$id": "NxExpoEasUpdate",
|
||||||
|
"cli": "nx",
|
||||||
|
"title": "Expo EAS Update executor",
|
||||||
|
"description": "Start an EAS update for your expo project",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"branch": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Branch to publish the update group on"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A short message describing the update"
|
||||||
|
},
|
||||||
|
"republish": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable JSON output, non-JSON messages will be printed to stderr",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Update group to republish"
|
||||||
|
},
|
||||||
|
"inputDir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Location of the bundle"
|
||||||
|
},
|
||||||
|
"skipBundler": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Skip running Expo CLI to bundle the app before publishing",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"enum": ["ios", "android", "all"],
|
||||||
|
"alias": "p",
|
||||||
|
"description": "The platform to build the app, example values: ios, android, all.",
|
||||||
|
"default": "all"
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable JSON output, non-JSON messages will be printed to stderr",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"auto": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Use the current git branch and commit message for the EAS branch and update message",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"privateKeyPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named \"private-key.pem\" in the certificate's directory."
|
||||||
|
},
|
||||||
|
"nonInteractive": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Run command in non-interactive mode",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
74
packages/expo/src/executors/update/update.impl.ts
Normal file
74
packages/expo/src/executors/update/update.impl.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { ExecutorContext, names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ChildProcess, fork } from 'child_process';
|
||||||
|
|
||||||
|
import { ensureNodeModulesSymlink } from '../../utils/ensure-node-modules-symlink';
|
||||||
|
|
||||||
|
import { ExpoEasUpdateOptions } from './schema';
|
||||||
|
|
||||||
|
export interface ReactNativeUpdateOutput {
|
||||||
|
success: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childProcess: ChildProcess;
|
||||||
|
|
||||||
|
export default async function* buildExecutor(
|
||||||
|
options: ExpoEasUpdateOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
): AsyncGenerator<ReactNativeUpdateOutput> {
|
||||||
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
ensureNodeModulesSymlink(context.root, projectRoot);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runCliUpdate(context.root, projectRoot, options);
|
||||||
|
yield { success: true };
|
||||||
|
} finally {
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCliUpdate(
|
||||||
|
workspaceRoot: string,
|
||||||
|
projectRoot: string,
|
||||||
|
options: ExpoEasUpdateOptions
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
childProcess = fork(
|
||||||
|
join(workspaceRoot, './node_modules/eas-cli/bin/run'),
|
||||||
|
['update', ...createUpdateOptions(options)],
|
||||||
|
{ cwd: join(workspaceRoot, projectRoot) }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure the child process is killed when the parent exits
|
||||||
|
process.on('exit', () => childProcess.kill());
|
||||||
|
process.on('SIGTERM', () => childProcess.kill());
|
||||||
|
|
||||||
|
childProcess.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
childProcess.on('exit', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUpdateOptions(options: ExpoEasUpdateOptions) {
|
||||||
|
return Object.keys(options).reduce((acc, k) => {
|
||||||
|
const v = options[k];
|
||||||
|
if (typeof v === 'boolean') {
|
||||||
|
if (v === true) {
|
||||||
|
// when true, does not need to pass the value true, just need to pass the flag in kebob case
|
||||||
|
acc.push(`--${names(k).fileName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc.push(`--${names(k).fileName}`, v);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
92
packages/expo/src/generators/application/application.spec.ts
Normal file
92
packages/expo/src/generators/application/application.spec.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import {
|
||||||
|
Tree,
|
||||||
|
readWorkspaceConfiguration,
|
||||||
|
getProjects,
|
||||||
|
readJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import { Linter } from '@nrwl/linter';
|
||||||
|
import { expoApplicationGenerator } from './application';
|
||||||
|
|
||||||
|
describe('app', () => {
|
||||||
|
let appTree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
appTree = createTreeWithEmptyWorkspace();
|
||||||
|
appTree.write('.gitignore', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update workspace.json', async () => {
|
||||||
|
await expoApplicationGenerator(appTree, {
|
||||||
|
name: 'myApp',
|
||||||
|
displayName: 'myApp',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
skipFormat: false,
|
||||||
|
js: false,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
});
|
||||||
|
const workspaceJson = readWorkspaceConfiguration(appTree);
|
||||||
|
const projects = getProjects(appTree);
|
||||||
|
|
||||||
|
expect(projects.get('my-app').root).toEqual('apps/my-app');
|
||||||
|
expect(workspaceJson.defaultProject).toEqual('my-app');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update nx.json', async () => {
|
||||||
|
await expoApplicationGenerator(appTree, {
|
||||||
|
name: 'myApp',
|
||||||
|
displayName: 'myApp',
|
||||||
|
tags: 'one,two',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
skipFormat: false,
|
||||||
|
js: false,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectConfiguration = readProjectConfiguration(appTree, 'my-app');
|
||||||
|
expect(projectConfiguration).toMatchObject({
|
||||||
|
tags: ['one', 'two'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate files', async () => {
|
||||||
|
await expoApplicationGenerator(appTree, {
|
||||||
|
name: 'myApp',
|
||||||
|
displayName: 'myApp',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
skipFormat: false,
|
||||||
|
js: false,
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
});
|
||||||
|
expect(appTree.exists('apps/my-app/src/app/App.tsx')).toBeTruthy();
|
||||||
|
expect(appTree.exists('apps/my-app/src/app/App.spec.tsx')).toBeTruthy();
|
||||||
|
|
||||||
|
const tsconfig = readJson(appTree, 'apps/my-app/tsconfig.json');
|
||||||
|
expect(tsconfig.extends).toEqual('../../tsconfig.base.json');
|
||||||
|
|
||||||
|
expect(appTree.exists('apps/my-app/.eslintrc.json')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate js files', async () => {
|
||||||
|
await expoApplicationGenerator(appTree, {
|
||||||
|
name: 'myApp',
|
||||||
|
displayName: 'myApp',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
skipFormat: false,
|
||||||
|
js: true,
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
});
|
||||||
|
expect(appTree.exists('apps/my-app/src/app/App.js')).toBeTruthy();
|
||||||
|
expect(appTree.exists('apps/my-app/src/app/App.spec.js')).toBeTruthy();
|
||||||
|
|
||||||
|
const tsconfig = readJson(appTree, 'apps/my-app/tsconfig.json');
|
||||||
|
expect(tsconfig.extends).toEqual('../../tsconfig.base.json');
|
||||||
|
|
||||||
|
expect(appTree.exists('apps/my-app/.eslintrc.json')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
59
packages/expo/src/generators/application/application.ts
Normal file
59
packages/expo/src/generators/application/application.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
|
import {
|
||||||
|
convertNxGenerator,
|
||||||
|
Tree,
|
||||||
|
formatFiles,
|
||||||
|
joinPathFragments,
|
||||||
|
GeneratorCallback,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
import { addLinting } from '../../utils/add-linting';
|
||||||
|
import { addJest } from '../../utils/add-jest';
|
||||||
|
import { runSymlink } from '../../utils/symlink-task';
|
||||||
|
|
||||||
|
import { normalizeOptions } from './lib/normalize-options';
|
||||||
|
import initGenerator from '../init/init';
|
||||||
|
import { addProject } from './lib/add-project';
|
||||||
|
import { addDetox } from './lib/add-detox';
|
||||||
|
import { createApplicationFiles } from './lib/create-application-files';
|
||||||
|
import { Schema } from './schema';
|
||||||
|
|
||||||
|
export async function expoApplicationGenerator(
|
||||||
|
host: Tree,
|
||||||
|
schema: Schema
|
||||||
|
): Promise<GeneratorCallback> {
|
||||||
|
const options = normalizeOptions(schema);
|
||||||
|
|
||||||
|
createApplicationFiles(host, options);
|
||||||
|
addProject(host, options);
|
||||||
|
|
||||||
|
const initTask = await initGenerator(host, { ...options, skipFormat: true });
|
||||||
|
const lintTask = await addLinting(
|
||||||
|
host,
|
||||||
|
options.projectName,
|
||||||
|
options.appProjectRoot,
|
||||||
|
[joinPathFragments(options.appProjectRoot, 'tsconfig.app.json')],
|
||||||
|
options.linter,
|
||||||
|
options.setParserOptionsProject
|
||||||
|
);
|
||||||
|
const jestTask = await addJest(
|
||||||
|
host,
|
||||||
|
options.unitTestRunner,
|
||||||
|
options.projectName,
|
||||||
|
options.appProjectRoot,
|
||||||
|
options.js
|
||||||
|
);
|
||||||
|
const detoxTask = await addDetox(host, options);
|
||||||
|
const symlinkTask = runSymlink(host.root, options.appProjectRoot);
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runTasksInSerial(initTask, lintTask, jestTask, detoxTask, symlinkTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default expoApplicationGenerator;
|
||||||
|
export const expoApplicationSchematic = convertNxGenerator(
|
||||||
|
expoApplicationGenerator
|
||||||
|
);
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"name": "<%= displayName %>",
|
||||||
|
"slug": "<%= projectName %>",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"icon": "./assets/icon.png",
|
||||||
|
"splash": {
|
||||||
|
"image": "./assets/splash.png",
|
||||||
|
"resizeMode": "contain",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"updates": {
|
||||||
|
"fallbackToCacheTimeout": 0
|
||||||
|
},
|
||||||
|
"assetBundlePatterns": [
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"foregroundImage": "./assets/adaptive-icon.png",
|
||||||
|
"backgroundColor": "#FFFFFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"favicon": "./assets/favicon.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
packages/expo/src/generators/application/files/assets/icon.png
Normal file
BIN
packages/expo/src/generators/application/files/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
packages/expo/src/generators/application/files/assets/logo.png
Normal file
BIN
packages/expo/src/generators/application/files/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
packages/expo/src/generators/application/files/assets/splash.png
Normal file
BIN
packages/expo/src/generators/application/files/assets/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user