@startupjs/cli 0.55.0-alpha.1 → 0.55.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ yarn add @startupjs/cli
14
14
  npx startupjs init myapp --version=next && cd myapp
15
15
  ```
16
16
 
17
- ## Licence
17
+ ## License
18
18
 
19
19
  MIT
20
20
 
package/index.js CHANGED
@@ -5,14 +5,9 @@ const fs = require('fs')
5
5
  const Font = require('fonteditor-core').Font
6
6
  const template = require('lodash/template')
7
7
  const CLI_VERSION = require('./package.json').version
8
- const DETOXRC_TEMPLATE = require('./detoxTemplates/detoxrcTemplate')
9
- const ENVDETOX_TEMPLATE = require('./detoxTemplates/envdetoxTemplate')
10
- const FIRSTTEST_TEMPLATE = require('./detoxTemplates/firstTestTemplate')
11
8
 
12
9
  const IS_PRERELEASE = /(?:alpha|canary)/.test(CLI_VERSION)
13
10
  const STARTUPJS_VERSION = IS_PRERELEASE ? `^${CLI_VERSION.replace(/\.\d+$/, '.0')}` : 'latest'
14
- const APP_JSON_PATH = path.join(process.cwd(), 'app.json')
15
- const ROOT_PATH = process.env.ROOT_PATH || process.cwd()
16
11
 
17
12
  let PATCHES_DIR
18
13
  try {
@@ -28,6 +23,17 @@ try {
28
23
  PATCHES_DIR = './patches'
29
24
  }
30
25
 
26
+ let PM_SCRIPTS_PATH
27
+ try {
28
+ PM_SCRIPTS_PATH = path.join(
29
+ path.dirname(require.resolve('@startupjs/pm')),
30
+ 'scripts.sh'
31
+ )
32
+ } catch (err) {
33
+ console.error(err)
34
+ console.error('ERROR!!! @startupjs/pm package wasn\'t found.')
35
+ }
36
+
31
37
  let PATCHES_GESTURE_HANDLER_DIR
32
38
  try {
33
39
  PATCHES_GESTURE_HANDLER_DIR = path.join(
@@ -89,214 +95,44 @@ const REMOVE_FILES = [
89
95
 
90
96
  const SCRIPTS_ORIG = {}
91
97
 
92
- // Web
93
-
94
- SCRIPTS_ORIG.web = ({ reset, vite, webpack } = {}) => {
95
- if (vite && !webpack) {
96
- return SCRIPTS_ORIG.webVite({ reset })
97
- } else {
98
- return SCRIPTS_ORIG.webWebpack
99
- }
100
- }
101
-
102
- SCRIPTS_ORIG.webVite = ({ reset } = {}) => oneLine(`
103
- ${reset ? 'npx rimraf node_modules/.vite_opt_cache &&' : ''}
104
- npx cross-env VITE_WEB=1
105
- npx vite
106
- `)
107
-
108
- SCRIPTS_ORIG.webWebpack = oneLine(`
109
- npx cross-env WEBPACK_DEV=1
110
- npx webpack-dev-server --config webpack.web.config.cjs
111
- `)
112
-
113
- // Detox test
114
-
115
- SCRIPTS_ORIG.test = ({ ios, init, build, artifacts, updateScreenshot } = {}) => {
116
- const appName = require(APP_JSON_PATH).name
117
-
118
- if (init) {
119
- try {
120
- SCRIPTS_ORIG.testInit()
121
- } catch (err) {
122
- return oneLine(`
123
- echo '\\033[0;31m${err}'
124
- `)
125
- }
126
-
127
- return `
128
- echo "
129
- \\033[0;32mCreated a directory at path: /e2e
130
- Created a file at path: /e2e/firstTest.e2e.js
131
- Created a file at path: /.env.detox
132
- Created a file at path: /.detoxrc.js
133
- "
134
- `
135
- }
136
-
137
- if (build) {
138
- return SCRIPTS_ORIG.testBuild(appName)
139
- }
140
-
141
- if (ios) {
142
- return SCRIPTS_ORIG.testIos(appName, artifacts, updateScreenshot)
143
- }
144
- }
145
-
146
- SCRIPTS_ORIG.testInit = () => {
147
- const detoxrcPath = path.join(process.cwd(), '.detoxrc.js')
148
- const envdetoxPath = path.join(process.cwd(), '.env.detox')
149
- const e2eDirPath = path.join(process.cwd(), 'e2e')
150
-
151
- fs.writeFileSync(detoxrcPath, DETOXRC_TEMPLATE)
152
- fs.writeFileSync(envdetoxPath, ENVDETOX_TEMPLATE)
153
- if (!fs.existsSync(e2eDirPath)) {
154
- fs.mkdirSync(e2eDirPath)
155
- }
156
- fs.writeFileSync(path.join(e2eDirPath, 'firstTest.e2e.js'), FIRSTTEST_TEMPLATE)
157
- }
98
+ // Development
158
99
 
159
- SCRIPTS_ORIG.testBuild = appName => oneLine(`
160
- ${SCRIPTS_ORIG.testJsBundle(appName)}
161
- && npx detox build -c ios
162
- `)
163
-
164
- SCRIPTS_ORIG.testIos = (appName, artifacts, updateScreenshot) => oneLine(`
165
- ${updateScreenshot ? `npx rimraf \`find ${ROOT_PATH} -name "__image_snapshots__" -type d\` &&` : ''}
100
+ SCRIPTS_ORIG.start = ({ inspect } = {}) => oneLine(`
166
101
  npx concurrently
167
- -s first -k -n "S,T"
168
- -c white,cyan.bgBlue
169
- "mongo ${appName}_test --eval 'db.dropDatabase();'
170
- && npx cross-env ASYNC=1 startupjs build
171
- && PORT=3001 MONGO_URL=mongodb://localhost:27017/${appName}_test npx startupjs start-production"
172
- "${SCRIPTS_ORIG.testJsBundle(appName)}
173
- && npx wait-on http://localhost:3001
174
- && npx detox test -c ios
175
- ${artifacts ? '--artifacts-location $PWD/artifacts --take-screenshots all' : ''}
176
- ${updateScreenshot ? '--testNamePattern Screenshots:' : ''}"
177
- `)
178
-
179
- SCRIPTS_ORIG.testJsBundle = appName => oneLine(`
180
- mkdir -p ios/build/Build/Products/Release-iphonesimulator/${appName}.app/
181
- && npx cross-env APP_ENV=detox react-native bundle
182
- --entry-file="index.js"
183
- --bundle-output="./ios/build/Build/Products/Release-iphonesimulator/${appName}.app/main.jsbundle"
184
- --reset-cache --dev=false
102
+ "${SCRIPTS_ORIG.web}"
103
+ "${SCRIPTS_ORIG.server({ inspect })}"
185
104
  `)
186
105
 
187
- // Server
188
-
189
- SCRIPTS_ORIG.server = ({ inspect, vite, webpack, pure } = {}) => {
190
- if (pure && !webpack) {
191
- return SCRIPTS_ORIG.serverPure({ inspect, vite })
192
- } else {
193
- return SCRIPTS_ORIG.serverWebpack({ inspect, vite })
194
- }
195
- }
196
-
197
- SCRIPTS_ORIG.serverPure = ({ inspect, vite } = {}) => oneLine(`
198
- ${vite ? 'npx cross-env VITE=1' : ''}
199
- npx nodemon
200
- --experimental-specifier-resolution=node
201
- ${inspect ? '--inspect' : ''}
202
- -e js,mjs,cjs,json,yaml server.js
203
- --delay 3
204
- --watch model/
205
- --watch hooks/
206
- --watch cron/
207
- --watch helpers/
208
- --watch serverHelpers/
209
- --watch isomorphicHelpers/
210
- --watch '**/routes.js'
211
- --watch server/
212
- --watch config.json
213
- `)
214
-
215
- SCRIPTS_ORIG.serverWebpack = (options) => oneLine(`
216
- npx concurrently
217
- -r -s first -k -n 'S,B'
218
- -c black.bgWhite,black.bgWhite
219
- "${SCRIPTS_ORIG.serverWebpackRun(options)}"
220
- "${SCRIPTS_ORIG.serverWebpackBuild}"
221
- `)
106
+ // Web
222
107
 
223
- SCRIPTS_ORIG.serverWebpackBuild = oneLine(`
108
+ SCRIPTS_ORIG.web = oneLine(`
224
109
  npx cross-env WEBPACK_DEV=1
225
- npx webpack --watch --config webpack.server.config.cjs
110
+ npx webpack-dev-server --config webpack.web.config.js
226
111
  `)
227
112
 
228
- SCRIPTS_ORIG.serverWebpackRun = ({ inspect, vite }) => oneLine(`
229
- npx rimraf ./build/server.dev.cjs &&
230
- npx just-wait -t 1000 --pattern ./build/server.dev.cjs &&
231
- ${vite ? 'npx cross-env VITE=1' : ''}
232
- npx nodemon
233
- --experimental-specifier-resolution=node
234
- ${inspect ? '--inspect' : ''}
235
- ./build/server.dev.cjs
236
- -r source-map-support/register
237
- -e js,mjs,cjs,json,yaml
238
- --watch ./build/server.dev.cjs
239
- `)
240
-
241
- // Start (web and server)
242
-
243
- SCRIPTS_ORIG.start = (options = {}) => {
244
- if (options.pure && !options.webpack) {
245
- return SCRIPTS_ORIG.startPure(options)
246
- } else {
247
- return SCRIPTS_ORIG.startWebpack(options)
248
- }
249
- }
250
-
251
- SCRIPTS_ORIG.startPure = (...args) => oneLine(`
252
- npx concurrently
253
- -s first -k -n 'Server,Web'
254
- -c cyan.bgBlue,gray
255
- "${SCRIPTS_ORIG.server(...args)}"
256
- "${SCRIPTS_ORIG.web(...args)}"
257
- `)
113
+ // Server
258
114
 
259
- SCRIPTS_ORIG.startWebpack = (options) => oneLine(`
260
- npx concurrently
261
- -p "{name}:"
262
- -s first -k -n 'Server,ServerBuild,Web'
263
- -c cyan.bgBlue.bold,gray,gray
264
- "${SCRIPTS_ORIG.serverWebpackRun(options)}"
265
- "${SCRIPTS_ORIG.serverWebpackBuild}"
266
- "${SCRIPTS_ORIG.web(options)}"
115
+ SCRIPTS_ORIG.server = ({ inspect } = {}) => oneLine(`
116
+ node
117
+ ${inspect ? '--inspect' : ''}
118
+ --watch
119
+ server.js
267
120
  `)
268
121
 
269
- // Production build
122
+ // Production
270
123
 
271
- SCRIPTS_ORIG.build = ({ async, pure } = {}) => oneLine(`
124
+ SCRIPTS_ORIG.build = ({ async } = {}) => oneLine(`
272
125
  npx rimraf ./build &&
273
- ${pure ? '' : 'npx webpack --config webpack.server.config.cjs &&'}
274
126
  ${async ? 'npx cross-env ASYNC=1' : ''}
275
- npx webpack --config webpack.web.config.cjs
127
+ npx webpack --config webpack.web.config.js
276
128
  `)
277
129
 
278
- SCRIPTS_ORIG.startProduction = ({ pure }) => {
279
- if (pure) {
280
- return SCRIPTS_ORIG.startProductionPure
281
- } else {
282
- return SCRIPTS_ORIG.startProductionWebpack
283
- }
284
- }
285
-
286
- SCRIPTS_ORIG.startProductionPure = oneLine(`
130
+ SCRIPTS_ORIG.startProduction = oneLine(`
287
131
  npx cross-env NODE_ENV=production
288
- node
289
- --experimental-specifier-resolution=node
290
- server.js
132
+ node server.js
291
133
  `)
292
134
 
293
- SCRIPTS_ORIG.startProductionWebpack = oneLine(`
294
- npx cross-env NODE_ENV=production
295
- node
296
- --experimental-specifier-resolution=node
297
- -r source-map-support/register
298
- build/server.cjs
299
- `)
135
+ // Etc
300
136
 
301
137
  SCRIPTS_ORIG.patchPackage = () => oneLine(`
302
138
  npx patch-package --patch-dir ${PATCHES_DIR} && ${SCRIPTS_ORIG.patchGestureHandler()}
@@ -408,8 +244,10 @@ commander
408
244
 
409
245
  // init react-native application
410
246
  await execa('npx', [
247
+ '--yes',
411
248
  `react-native${'@' + reactNative}`,
412
249
  'init',
250
+ '--skip-install',
413
251
  projectName
414
252
  ].concat(['--version', reactNative]), {
415
253
  cwd: path.join(process.cwd(), LOCAL_DIR),
@@ -453,7 +291,7 @@ commander
453
291
 
454
292
  // copy additional startupjs template files over react-native ones
455
293
  console.log(`> Copy template '${template}'`)
456
- for (let subTemplate of TEMPLATES[template].subTemplates) {
294
+ for (const subTemplate of TEMPLATES[template].subTemplates) {
457
295
  const subTemplatePath = path.join(templatesPath, subTemplate)
458
296
  await recursivelyCopyFiles(subTemplatePath, projectPath)
459
297
  }
@@ -516,10 +354,6 @@ commander
516
354
  .command('start')
517
355
  .description('Run "startupjs web" and "startupjs server" at the same time.')
518
356
  .option('-i, --inspect', 'Use node --inspect')
519
- .option('-p, --pure', 'Don\'t use any build system for node')
520
- .option('-v, --vite', 'Use ES Modules and Vite for development instead of Webpack')
521
- .option('-w, --webpack', 'Force use Webpack. This will take priority over --vite and --pure option.')
522
- .option('-r, --reset', 'Reset Vite cache before starting the server. This is helpful when you are directly monkey-patching node_modules')
523
357
  .action(async (options) => {
524
358
  await execa.command(
525
359
  SCRIPTS_ORIG.start(options),
@@ -528,31 +362,19 @@ commander
528
362
  })
529
363
 
530
364
  commander
531
- .command('test')
532
- .description('Init or run E2E Detox tests')
533
- .option('-s, --ios', 'Run tests on iOS simulator')
534
- .option('-i, --init', 'Init test environment in your project')
535
- .option('-b, --build', 'Build ios app /ios/build')
536
- .option('-a, --artifacts', 'Artifacts are disabled by default. To enable them, pass this flag') // https://github.com/wix/Detox/blob/master/docs/APIRef.Artifacts.md
537
- .option('-u, --updateScreenshot', 'Update tests screenshots')
365
+ .command('web')
366
+ .description('Compile (with webpack) and run web')
538
367
  .action(async (options) => {
539
- try {
540
- await execa.command(
541
- SCRIPTS_ORIG.test(options),
542
- { stdio: 'inherit', shell: true }
543
- )
544
- } catch (err) {
545
- console.error('Something went wrong...')
546
- }
368
+ await execa.command(
369
+ SCRIPTS_ORIG.web,
370
+ { stdio: 'inherit', shell: true }
371
+ )
547
372
  })
548
373
 
549
374
  commander
550
375
  .command('server')
551
376
  .description('Compile (with webpack) and run server')
552
377
  .option('-i, --inspect', 'Use node --inspect')
553
- .option('-p, --pure', 'Don\'t use any build system')
554
- .option('-w, --webpack', 'Force use Webpack for server build. This takes priority over --pure option')
555
- .option('-v, --vite', 'Automatically redirect to the web bundle served by Vite. Use this when running Vite for web client')
556
378
  .action(async (options) => {
557
379
  await execa.command(
558
380
  SCRIPTS_ORIG.server(options),
@@ -563,7 +385,6 @@ commander
563
385
  commander
564
386
  .command('build')
565
387
  .description('Build web bundles')
566
- .option('-p, --pure', 'Don\'t use any build system for node')
567
388
  .option('-a, --async', 'Build with splitting code into async chunks loaded dynamically')
568
389
  .action(async (options) => {
569
390
  await execa.command(
@@ -575,23 +396,9 @@ commander
575
396
  commander
576
397
  .command('start-production')
577
398
  .description('Start production')
578
- .option('-p, --pure', 'Don\'t use any build system for node')
579
- .action(async (options) => {
580
- await execa.command(
581
- SCRIPTS_ORIG.startProduction(options),
582
- { stdio: 'inherit', shell: true }
583
- )
584
- })
585
-
586
- commander
587
- .command('web')
588
- .description('Run web bundling (Webpack). Insead of bundling you can also use Vite and ES Modules by specifying --vite')
589
- .option('-v, --vite', 'Use ES Modules and Vite for development instead of Webpack')
590
- .option('-w, --webpack', 'Force use Webpack. This takes priority over --vite option.')
591
- .option('-r, --reset', 'Reset Vite cache before starting the server. This is helpful when you are directly monkey-patching node_modules')
592
399
  .action(async (options) => {
593
400
  await execa.command(
594
- SCRIPTS_ORIG.web(options),
401
+ SCRIPTS_ORIG.startProduction,
595
402
  { stdio: 'inherit', shell: true }
596
403
  )
597
404
  })
@@ -647,6 +454,30 @@ commander
647
454
  )
648
455
  })
649
456
 
457
+ // ----- project management commands
458
+
459
+ commander
460
+ .command('init-pm')
461
+ .description('Create a new project on github from template')
462
+ .action(async () => {
463
+ await execa.command(`${PM_SCRIPTS_PATH} init-pm`, { shell: true, stdio: 'inherit' })
464
+ addPmScriptsToPackageJson() // add `yarn pm` and `yarn task` to package.json/scripts
465
+ })
466
+
467
+ commander
468
+ .command('task <issueNumber>')
469
+ .description('Create a task branch (or just switch to it if it already exists)')
470
+ .action(async (issueNumber) => {
471
+ await execa.command(`${PM_SCRIPTS_PATH} task ${issueNumber}`, { shell: true, stdio: 'inherit' })
472
+ })
473
+
474
+ commander
475
+ .command('pr [issueNumber]')
476
+ .description('Make PR for this task (or re-request review if it already exists)')
477
+ .action(async (issueNumber = '') => {
478
+ await execa.command(`${PM_SCRIPTS_PATH} pr ${issueNumber}`, { shell: true, stdio: 'inherit' })
479
+ })
480
+
650
481
  // ----- helpers
651
482
 
652
483
  async function recursivelyCopyFiles (sourcePath, targetPath) {
@@ -654,10 +485,10 @@ async function recursivelyCopyFiles (sourcePath, targetPath) {
654
485
 
655
486
  if (fileNames.length === 0) return
656
487
 
657
- for (let fileName of fileNames) {
658
- let filePath = path.join(sourcePath, fileName)
488
+ for (const fileName of fileNames) {
489
+ const filePath = path.join(sourcePath, fileName)
659
490
  if (fs.lstatSync(filePath).isDirectory()) {
660
- let subTargetPath = path.join(targetPath, fileName)
491
+ const subTargetPath = path.join(targetPath, fileName)
661
492
  await execa(
662
493
  'mkdir',
663
494
  ['-p', subTargetPath],
@@ -715,7 +546,6 @@ function patchScriptsInPackageJson (projectPath) {
715
546
  delete packageJSON.devDependencies['babel-jest']
716
547
  delete packageJSON.devDependencies['react-test-renderer']
717
548
  delete packageJSON.devDependencies.jest
718
- delete packageJSON.jest
719
549
 
720
550
  packageJSON.scripts = {
721
551
  ...packageJSON.scripts,
@@ -746,6 +576,23 @@ function patchScriptsInPackageJson (projectPath) {
746
576
  )
747
577
  }
748
578
 
579
+ function addPmScriptsToPackageJson () {
580
+ const projectPath = process.cwd()
581
+ const packageJSONPath = path.join(projectPath, 'package.json')
582
+ const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString())
583
+
584
+ packageJSON.scripts = {
585
+ ...packageJSON.scripts,
586
+ task: 'npx startupjs task',
587
+ pr: 'npx startupjs pr'
588
+ }
589
+
590
+ fs.writeFileSync(
591
+ packageJSONPath,
592
+ `${JSON.stringify(packageJSON, null, 2)}\n`
593
+ )
594
+ }
595
+
749
596
  function appendGitignore (projectPath) {
750
597
  const gitignorePath = path.join(projectPath, '.gitignore')
751
598
  let gitignore = fs.readFileSync(gitignorePath).toString()
@@ -755,13 +602,12 @@ function appendGitignore (projectPath) {
755
602
  /data/
756
603
  # Protection from accidentally commiting private npm keys to a public repo
757
604
  .npmrc
758
- # Detox
759
- /artifacts/
760
- /e2e/__diff_output__
761
605
  # Mongo data when running in a docker dev container
762
606
  /.mongo
763
607
  # yarn
764
608
  .yarn/
609
+ # sqlite
610
+ sqlite.db
765
611
  `.replace(/\n\s+/g, '\n')
766
612
 
767
613
  fs.writeFileSync(gitignorePath, gitignore)
@@ -4,21 +4,12 @@ const { pushNotificationsLinkerAndroid } = require('./pushNotificationsLinker')
4
4
  // uncomment when the android in Detox will be implemented
5
5
  // const DetoxLinker = require('./DetoxLinker')
6
6
 
7
- // it isn't need at currunt version because we use react-native-tab-view@3.0.0
8
- // const ActivityLinker = require('./activityLinker')
9
-
10
7
  module.exports = () => {
11
8
  infon('\nRunning Android postlink script\n')
12
9
 
13
- // React-native-gesture-handler is not used in the current version, but it is possible to link for it in the future
14
- // new ActivityLinker().link()
15
- // logn()
16
-
17
10
  new StylesLinker().link()
18
11
 
19
12
  logn()
20
13
  pushNotificationsLinkerAndroid()
21
- // logn()
22
- // new DetoxLinker().link()
23
14
  logn('\n')
24
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs/cli",
3
- "version": "0.55.0-alpha.1",
3
+ "version": "0.55.0-alpha.12",
4
4
  "description": "start, watch and build startupjs project",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@
8
8
  "access": "public"
9
9
  },
10
10
  "dependencies": {
11
+ "@startupjs/pm": "^0.55.0-alpha.10",
11
12
  "commander": "^3.0.1",
12
13
  "concurrently": "^6.4.0",
13
14
  "cross-env": "^7.0.3",
@@ -25,5 +26,5 @@
25
26
  "webpack-cli": "^4.9.1",
26
27
  "webpack-dev-server": "^4.4.0"
27
28
  },
28
- "gitHead": "f9f14627172890e54490aa3717e21f19614a958b"
29
+ "gitHead": "d9fdf45341ca733978cdc4fa2562055922e0c2ed"
29
30
  }
@@ -1,26 +0,0 @@
1
- module.exports = `const { name } = require('./app.json')
2
- const { configPath } = require('@startupjs/e2e')
3
-
4
- module.exports = {
5
- "testRunner": "jest",
6
- "runnerConfig": configPath,
7
- "configurations": {
8
- "ios": {
9
- "type": "ios.simulator",
10
- "binaryPath": \`ios/build/Build/Products/Release-iphonesimulator/\${name}.app\`,
11
- "build": \`xcodebuild -workspace ios/\${name}.xcworkspace -scheme \${name} -configuration Release -sdk iphonesimulator -derivedDataPath ios/build\`,
12
- "device": {
13
- "type": "iPhone 11"
14
- }
15
- },
16
- "android": {
17
- "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
18
- "build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
19
- "type": "android.emulator",
20
- "device": {
21
- "avdName": "Pixel_2_API_28"
22
- }
23
- }
24
- }
25
- }
26
- `
@@ -1 +0,0 @@
1
- module.exports = 'BASE_URL=http://localhost:3001'
@@ -1,18 +0,0 @@
1
- module.exports = `const { x } = require('@startupjs/e2e/helpers')
2
-
3
- describe('Example', () => {
4
- it('should have welcome screen', async () => {
5
- await x('#welcome').toBeVisible()
6
- })
7
-
8
- it('should show hello screen after tap', async () => {
9
- await x('#hello_button').tap()
10
- await x('=Hello!!!').toBeVisible()
11
- })
12
-
13
- it('should show world screen after tap', async () => {
14
- await x('#world_button').tap()
15
- await x('=World!!!').toBeVisible()
16
- })
17
- })
18
- `
@@ -1,220 +0,0 @@
1
- const fs = require('fs')
2
- const path = require('./path')
3
- const { DetoxTest, networkSecurityConfig } = require('./templates')
4
- const { errorn, warnn, logn, infon, debugn } = require('./log')
5
-
6
- class DetoxLinker {
7
- constructor () {
8
- this.networkSecurityConfigFolder = path.networkSecurityConfigFolder
9
- this.networkSecurityConfigRout = path.networkSecurityConfigRout
10
- this.detoxTestFolder = path.detoxTestFolder
11
- this.detoxTestRoute = path.detoxTestRoute
12
- this.mainBuildGradleAndroid = path.mainBuildGradleAndroid
13
- this.appBuildGradleAndroid = path.appBuildGradleAndroid
14
- this.androidManifestXML = path.androidManifestXML
15
- }
16
-
17
- link () {
18
- if (!this.checkRouts()) return
19
-
20
- logn(' Linking build.gradle')
21
- this.linkMainBuildGradle()
22
-
23
- logn(' Linking app/build.gradle')
24
- this.linkAppBuildGradleAndroid()
25
-
26
- logn(' Linking androidManifestXML')
27
- this.linkAndroidManifestXML()
28
-
29
- logn(' create network_security_config.xml')
30
- this.createNetworkSecurityConfig()
31
-
32
- logn(' create DetoxTest.java')
33
- this.createDetoxTest()
34
- }
35
-
36
- linkMainBuildGradle () {
37
- try {
38
- let content = fs.readFileSync(this.mainBuildGradleAndroid, 'utf8')
39
- content = this._linkMainBuildGradle(content)
40
- fs.writeFileSync(this.mainBuildGradleAndroid, content)
41
- infon(' build.gradle linked successfully!')
42
- } catch (e) {
43
- errorn('build.gradle was not linked. ' + e.message)
44
- }
45
- }
46
-
47
- _linkMainBuildGradle (content) {
48
- if (this._isLinkMainBuildGradle(content)) {
49
- warnn(' build.gradle already linked')
50
- return content
51
- }
52
-
53
- const extRegExp = /ext\s*\{[\s\S]*?\}/
54
- const detoxExt = 'ext {\n' +
55
- ' buildToolsVersion = "29.0.2"\n' +
56
- ' minSdkVersion = 18\n' +
57
- ' compileSdkVersion = 29\n' +
58
- ' targetSdkVersion = 29\n' +
59
- ' kotlinVersion = "1.4.21"\n' +
60
- ' }'
61
-
62
- const dependenciesRegExp = /dependencies\s*\{[\s\S]*?\}/
63
- const dependencies = 'dependencies {\n' +
64
- ' classpath("com.android.tools.build:gradle:3.5.3")\n' +
65
- ' // NOTE: Do not place your application dependencies here; they belong\n' +
66
- ' // in the individual module build.gradle files\n' +
67
- ' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"\n' +
68
- ' }'
69
-
70
- const allProgectsPlaceForDetox = /(allprojects[\s\S]*)google\(\)/
71
- const allProgectsDetox = '\n maven {\n' +
72
- ' // All of Detox\' artifacts are provided via the npm module\n' +
73
- ' url "$rootDir/../node_modules/detox/Detox-android"\n' +
74
- ' }\n'
75
-
76
- debugn(' Linking detox files in build.gradle')
77
- return content.replace(extRegExp, detoxExt)
78
- .replace(dependenciesRegExp, dependencies)
79
- .replace(allProgectsPlaceForDetox, content.match(allProgectsPlaceForDetox)[0] + allProgectsDetox)
80
- }
81
-
82
- linkAppBuildGradleAndroid () {
83
- try {
84
- let content = fs.readFileSync(this.appBuildGradleAndroid, 'utf8')
85
- content = this._linkAppBuildGradleAndroid(content)
86
- fs.writeFileSync(this.appBuildGradleAndroid, content)
87
- infon(' app/build.gradle linked successfully!')
88
- } catch (e) {
89
- errorn('app/build.gradle was not linked. ' + e.message)
90
- }
91
- }
92
-
93
- _linkAppBuildGradleAndroid (content) {
94
- if (this._isLinkAppBuildGradleAndroid(content)) {
95
- warnn(' app/build.gradle already linked')
96
- return content
97
- }
98
-
99
- const defaultConfigRegExp = /defaultConfig\s*\{[\s\S]*?\}/
100
- const defaultConfigDetox = content.match(defaultConfigRegExp)[0].replace(/\}$/,
101
- ' testBuildType System.getProperty(\'testBuildType\', \'debug\') // This will later be used to control the test apk build type\n' +
102
- ' testInstrumentationRunner \'androidx.test.runner.AndroidJUnitRunner\'\n' +
103
- ' }'
104
- )
105
-
106
- const progvardRegExp = /proguardFiles getDefaultProguardFile\("proguard-android\.txt"\), "proguard-rules\.pro"/
107
- const progvardDetox = 'proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"\n' +
108
- ' // Detox-specific additions to pro-guard\n' +
109
- // eslint-disable-next-line no-template-curly-in-string
110
- ' proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"'
111
-
112
- const dependencesEndRegExp = /else\s*\{[\s\S]*?implementation jscFlavor[\s\S]*?\}/
113
- const dependencesDetox = content.match(dependencesEndRegExp)[0].replace(/\}$/,
114
- '}\n' +
115
- ' androidTestImplementation(\'com.wix:detox:+\')'
116
- )
117
- return content.replace(defaultConfigRegExp, defaultConfigDetox)
118
- .replace(progvardRegExp, progvardDetox)
119
- .replace(dependencesEndRegExp, dependencesDetox)
120
- }
121
-
122
- linkAndroidManifestXML () {
123
- try {
124
- let content = fs.readFileSync(this.androidManifestXML, 'utf8')
125
- content = this._linkAndroidManifestXML(content)
126
- fs.writeFileSync(this.androidManifestXML, content)
127
- infon(' AndroidManifestXML linked successfully!')
128
- } catch (e) {
129
- errorn('AndroidManifestXML was not linked. ' + e.message)
130
- }
131
- }
132
-
133
- _linkAndroidManifestXML (content) {
134
- if (this._isLinkAndroidManifestXML(content)) {
135
- warnn(' AndroidManifestXML already linked')
136
- return content
137
- }
138
-
139
- const manifestApplicationEndRegExp = /<application[\s\S]*?>/
140
- const manifestApplicationEndDetox = content.match(manifestApplicationEndRegExp)[0].replace(/>$/,
141
- '\n android:networkSecurityConfig="@xml/network_security_config">'
142
- )
143
- return content.replace(manifestApplicationEndRegExp, manifestApplicationEndDetox)
144
- }
145
-
146
- createNetworkSecurityConfig () {
147
- try {
148
- if (fs.existsSync(this.networkSecurityConfigRout)) {
149
- warnn(' network_security_config.xml already exist')
150
- return
151
- }
152
- if (!fs.existsSync(this.networkSecurityConfigFolder)) {
153
- fs.mkdirSync(this.networkSecurityConfigFolder, { recursive: true })
154
- }
155
- fs.writeFileSync(this.networkSecurityConfigRout, networkSecurityConfig)
156
- infon(' network_security_config.xml created successfully!')
157
- } catch (e) {
158
- errorn('network_security_config.xml was not linked. ' + e.message)
159
- }
160
- }
161
-
162
- createDetoxTest () {
163
- try {
164
- if (fs.existsSync(this.detoxTestRoute)) {
165
- warnn(' DetoxTest.java already exist')
166
- return
167
- }
168
- if (!fs.existsSync(this.detoxTestFolder)) {
169
- fs.mkdirSync(this.detoxTestFolder, { recursive: true }, (err) => {
170
- if (err) throw err
171
- })
172
- }
173
- fs.writeFileSync(this.detoxTestRoute, DetoxTest)
174
- infon(' DetoxTest.java created successfully!')
175
- } catch (e) {
176
- errorn('DetoxTest.java was not linked. ' + e.message)
177
- }
178
- }
179
-
180
- _isLinkMainBuildGradle (content) {
181
- return (/kotlinVersion = "1\.4\.21"/.test(content) && /minSdkVersion=18/.test(content)) ||
182
- /classpath "org\.jetbrains\.kotlin:kotlin-gradle-plugin:\$kotlinVersion"/.test(content) ||
183
- /url "\$rootDir\/\.\.\/node_modules\/detox\/Detox-android"/.test(content)
184
- }
185
-
186
- _isLinkAppBuildGradleAndroid (content) {
187
- return /testBuildType System\.getProperty\('testBuildType', 'debug'\)/.test(content) ||
188
- /testInstrumentationRunner 'androidx\.test\.runner\.AndroidJUnitRunner'/.test(content) ||
189
- /proguardFile "\$\{rootProject\.projectDir\}\/\.\.\/node_modules\/detox\/android\/detox\/proguard-rules-app\.pro"/.test(content) ||
190
- /androidTestImplementation\('com\.wix:detox:\+'\)/.test(content)
191
- }
192
-
193
- _isLinkAndroidManifestXML (content) {
194
- return /android:networkSecurityConfig="@xml\/network_security_config"/.test(content)
195
- }
196
-
197
- checkRouts () {
198
- if (!this.mainBuildGradleAndroid) {
199
- errorn(
200
- 'build.gradle not found! Does the file exist in the correct folder?'
201
- )
202
- return false
203
- }
204
- if (!this.appBuildGradleAndroid) {
205
- errorn(
206
- 'app/build.gradle not found! Does the file exist in the correct folder?'
207
- )
208
- return false
209
- }
210
- if (!this.androidManifestXML) {
211
- errorn(
212
- 'main/AndroidManifest.xml not found! Does the file exist in the correct folder?'
213
- )
214
- return false
215
- }
216
- return true
217
- }
218
- }
219
-
220
- module.exports = DetoxLinker
@@ -1,66 +0,0 @@
1
- const fs = require('fs')
2
- const path = require('./path')
3
- const { errorn, warnn, logn, infon, debugn } = require('./log')
4
-
5
- class ActivityLinker {
6
- constructor () {
7
- this.path = path.mainActivityJava
8
- }
9
-
10
- link () {
11
- if (!this.path) {
12
- errorn(
13
- 'MainActivity not found! Does the file exist in the correct folder?'
14
- )
15
- return
16
- }
17
-
18
- logn(' Linking MainActivity')
19
-
20
- try {
21
- let content = fs.readFileSync(this.path, 'utf8')
22
- content = this._link(content)
23
- fs.writeFileSync(this.path, content)
24
- infon(' MainActivity linked successfully!')
25
- } catch (e) {
26
- errorn('MainActivity was not linked. ' + e.message)
27
- }
28
- }
29
-
30
- _link (content) {
31
- if (this._isGestureHandlerLinked(content)) {
32
- warnn(' react-native-gesture already linked')
33
- return content
34
- }
35
-
36
- debugn(' Linking react-native-gesture-handler')
37
- return content
38
- .replace(
39
- 'import com.facebook.react.ReactActivity;',
40
- 'import com.facebook.react.ReactActivity;\n' +
41
- 'import com.facebook.react.ReactActivityDelegate;\n' +
42
- 'import com.facebook.react.ReactRootView;\n' +
43
- 'import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;'
44
- )
45
- .replace(
46
- /\/\*\*\s*\n([^*]|(\*(?!\/)))*\*\/\s*@Override\s*protected\s*String\s*getMainComponentName\s*\(\)\s*{\s*return.+\s*\}/,
47
- '$&\n' +
48
- ' @Override\n' +
49
- ' protected ReactActivityDelegate createReactActivityDelegate() {\n' +
50
- ' return new ReactActivityDelegate(this, getMainComponentName()) {\n' +
51
- ' @Override\n' +
52
- ' protected ReactRootView createRootView() {\n' +
53
- ' return new RNGestureHandlerEnabledRootView(MainActivity.this);\n' +
54
- ' }\n' +
55
- ' };\n' +
56
- ' }'
57
- )
58
- }
59
-
60
- _isGestureHandlerLinked (content) {
61
- return /new\sRNGestureHandlerEnabledRootView\(MainActivity\.this\)/
62
- .test(content)
63
- }
64
- }
65
-
66
- module.exports = ActivityLinker
package/link/path.js DELETED
@@ -1,61 +0,0 @@
1
- const glob = require('glob')
2
- const path = require('path')
3
- const ignoreFolders = { ignore: ['node_modules/**', '**/build/**'] }
4
- const styles = glob.sync('**/values/styles.xml', ignoreFolders)
5
- const appJsonPath = glob.sync('**/app.json', ignoreFolders)[0]
6
- if (!appJsonPath) {
7
- throw new Error('Can\'t find app.json')
8
- }
9
- // TODO: some people get undefined when trying to find mainApplicationJava
10
- // const mainApplicationJava = glob.sync(
11
- // '**/MainApplication.java',
12
- // ignoreFolders
13
- // )[0]
14
-
15
- // Android files
16
- const mainActivityJava = glob.sync('**/MainActivity.java', ignoreFolders)[0]
17
-
18
- const mainBuildGradleAndroid = glob.sync(
19
- '**/android/build.gradle',
20
- ignoreFolders
21
- )[0]
22
- const appBuildGradleAndroid = glob.sync(
23
- '**/android/app/build.gradle',
24
- ignoreFolders
25
- )[0]
26
- const androidManifestXML = glob.sync(
27
- '**/main/AndroidManifest.xml',
28
- ignoreFolders
29
- )[0]
30
- const APP_NAME = require(path.resolve(process.cwd(), appJsonPath)).name
31
- const networkSecurityConfigFolder = 'android/app/src/main/res/xml'
32
- const networkSecurityConfigRout = networkSecurityConfigFolder + '/network_security_config.xml'
33
- const detoxTestFolder = `android/app/src/androidTest/java/com/${APP_NAME}`
34
- const detoxTestRoute = detoxTestFolder + '/DetoxTest.java'
35
-
36
- // iOS files
37
- const appDelegateH = glob.sync('**/AppDelegate.h', ignoreFolders)[0]
38
- const appDelegateM = glob.sync('**/AppDelegate.m', ignoreFolders)[0]
39
- const podFilePath = glob.sync('**/Podfile', ignoreFolders)[0]
40
-
41
- // exports
42
-
43
- exports.mainActivityJava = mainActivityJava
44
- // exports.mainApplicationJava = mainApplicationJava
45
- // exports.rootGradle = mainApplicationJava
46
- // .replace(/android\/app\/.*\.java/, 'android/build.gradle')
47
- exports.styles = styles
48
-
49
- exports.appDelegateH = appDelegateH
50
- exports.appDelegateM = appDelegateM
51
- exports.podFilePath = podFilePath
52
-
53
- exports.networkSecurityConfigFolder = networkSecurityConfigFolder
54
- exports.networkSecurityConfigRout = networkSecurityConfigRout
55
- exports.detoxTestFolder = detoxTestFolder
56
- exports.detoxTestRoute = detoxTestRoute
57
- exports.mainBuildGradleAndroid = mainBuildGradleAndroid
58
- exports.appBuildGradleAndroid = appBuildGradleAndroid
59
- exports.androidManifestXML = androidManifestXML
60
-
61
- exports.APP_NAME = APP_NAME
@@ -1,35 +0,0 @@
1
- const { APP_NAME } = require('../path')
2
-
3
- exports.DetoxTest = `// Replace "com.example" here and below with your app's package name from the top of MainActivity.java
4
- package com.${APP_NAME};
5
-
6
- import com.wix.detox.Detox;
7
- import com.wix.detox.config.DetoxConfig;
8
-
9
- import org.junit.Rule;
10
- import org.junit.Test;
11
- import org.junit.runner.RunWith;
12
-
13
- import androidx.test.ext.junit.runners.AndroidJUnit4;
14
- import androidx.test.filters.LargeTest;
15
- import androidx.test.rule.ActivityTestRule;
16
-
17
- @RunWith(AndroidJUnit4.class)
18
- @LargeTest
19
- public class DetoxTest {
20
- @Rule
21
- // Replace 'MainActivity' with the value of android:name entry in
22
- // <activity> in AndroidManifest.xml
23
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
24
-
25
- @Test
26
- public void runDetoxTests() {
27
- DetoxConfig detoxConfig = new DetoxConfig();
28
- detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
29
- detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
30
- detoxConfig.rnContextLoadTimeoutSec = (com.${APP_NAME}.BuildConfig.DEBUG ? 180 : 60);
31
-
32
- Detox.runTests(mActivityRule, detoxConfig);
33
- }
34
- }
35
- `
@@ -1,7 +0,0 @@
1
- const { DetoxTest } = require('./DetoxTest.java')
2
- const { networkSecurityConfig } = require('./network_security_config.xml')
3
-
4
- module.exports = {
5
- DetoxTest,
6
- networkSecurityConfig
7
- }
@@ -1,7 +0,0 @@
1
- exports.networkSecurityConfig = `<?xml version="1.0" encoding="utf-8"?>
2
- <network-security-config>
3
- <domain-config cleartextTrafficPermitted="true">
4
- <domain includeSubdomains="true">10.0.2.2</domain>
5
- <domain includeSubdomains="true">localhost</domain>
6
- </domain-config>
7
- </network-security-config>`