diff --git a/.eslintrc.js b/.eslintrc.js index 1c885b1..7668090 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,12 +2,22 @@ // Distributed under Apache 2.0 License. See LICENSE file in the project root for full license information. // ESLint config for magic-script-cli project. module.exports = { - "env": { - "es6": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": 2018 - }, - "extends": "semistandard" + env: { + es6: true, + node: true + }, + parserOptions: { + ecmaVersion: 2018 + }, + rules: { + 'space-before-function-paren': [ + 'error', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always' + } + ] + }, + extends: 'semistandard' }; diff --git a/__tests__/initutils.js b/__tests__/initutils.js index f4fe918..04c6824 100644 --- a/__tests__/initutils.js +++ b/__tests__/initutils.js @@ -2,6 +2,7 @@ jest.mock('fs'); jest.mock('../lib/util'); jest.mock('../lib/logger'); jest.mock('child_process'); +jest.mock('../lib/rename/rename.js'); const child_process = require('child_process'); jest.spyOn(child_process, 'spawnSync'); @@ -11,37 +12,61 @@ const mockedFs = require('fs'); const util = require('../lib/util'); const initUtil = require('../lib/initutils'); const logger = require('../lib/logger'); +const mockedRename = require('../lib/rename/rename'); afterEach(() => { jest.resetAllMocks(); }); describe('Test init utils methods', () => { - function createStat(isFile, isDirectory) { var statObject = {}; - statObject.isFile = function () { return isFile; }; - statObject.isDirectory = function () { return isDirectory; }; + statObject.isFile = function () { + return isFile; + }; + statObject.isDirectory = function () { + return isDirectory; + }; return statObject; } test('should update manifest package id in manifest file', () => { - let result = initUtil.updateManifest('com.magicleap.magicscript.hello-sample', 'com.test.sample', 'name', false); + let result = initUtil.updateManifest( + 'com.magicleap.magicscript.hello-sample', + 'com.test.sample', + 'name', + false + ); expect(result).toBe('com.test.sample'); }); test('should update manifest visible name in manifest file', () => { - let result = initUtil.updateManifest('MagicScript Hello Sample', 'com.test.sample', 'test name', false); + let result = initUtil.updateManifest( + 'MagicScript Hello Sample', + 'com.test.sample', + 'test name', + false + ); expect(result).toBe('test name'); }); test('should update manifest to fullscreen if is immersive', () => { - let result = initUtil.updateManifest('universe', 'com.test.sample', 'test name', true); + let result = initUtil.updateManifest( + 'universe', + 'com.test.sample', + 'test name', + true + ); expect(result).toBe('fullscreen'); }); test('should update manifest to Fullscreen if is immersive', () => { - let result = initUtil.updateManifest('Universe', 'com.test.sample', 'test name', true); + let result = initUtil.updateManifest( + 'Universe', + 'com.test.sample', + 'test name', + true + ); expect(result).toBe('Fullscreen'); }); @@ -56,20 +81,37 @@ describe('Test init utils methods', () => { }); test('should update components manifest privileges', () => { - let result = initUtil.updateComponentManifest(''); - expect(result).toBe('\n '); + let result = initUtil.updateComponentManifest( + '' + ); + expect(result).toBe( + '\n ' + ); }); test('should update manifest privileges if is immersive', () => { - let result = initUtil.updateManifest('', 'com.test.sample', 'test name', true); - expect(result).toBe('\n '); + let result = initUtil.updateManifest( + '', + 'com.test.sample', + 'test name', + true + ); + expect(result).toBe( + '\n ' + ); }); test('should create directory if does not exist when copying vanilla files', () => { mockedFs.existsSync.mockReturnValueOnce(false); mockedFs.readdirSync.mockReturnValueOnce([]); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', false, 'com.test.app', 'visibleName'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + false, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.existsSync).toHaveBeenCalledWith('dstPath'); expect(mockedFs.mkdirSync).toHaveBeenCalledWith('dstPath'); @@ -78,7 +120,7 @@ describe('Test init utils methods', () => { test('should create directory if does not exist when copying components files', () => { mockedFs.existsSync.mockReturnValueOnce(false); mockedFs.readdirSync.mockReturnValueOnce([]); - + initUtil.copyComponentFiles('srcPath', 'dstPath', false, ''); expect(mockedFs.existsSync).toHaveBeenCalledWith('dstPath'); @@ -88,122 +130,266 @@ describe('Test init utils methods', () => { test('should copy components manifest file with updated privilege when is immersive', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['manifest.xml', 'file']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('').mockReturnValueOnce('fileContent'); - + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('') + .mockReturnValueOnce('fileContent'); + initUtil.copyComponentFiles('srcPath', 'dstPath', true, ''); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); - expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/manifest.xml'); + expect(mockedFs.statSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/manifest.xml' + ); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/file'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/manifest.xml', 'utf8'); + + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/manifest.xml', + 'utf8' + ); expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/file'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/manifest.xml', '\n ', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/file', 'fileContent'); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/manifest.xml', + '\n ', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/file', + 'fileContent' + ); }); test('should change main.js or main.tsx file from landscape to immersive when is immersive', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['main.js', 'main.tsx']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('landscape').mockReturnValueOnce('landscape'); - + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('landscape') + .mockReturnValueOnce('landscape'); + initUtil.copyComponentFiles('srcPath', 'dstPath', true, ''); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/main.js'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/main.tsx'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/main.js', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/main.tsx', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/main.js', 'immersive', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/main.tsx', 'immersive', 'utf8'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/main.js', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/main.tsx', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/main.js', + 'immersive', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/main.tsx', + 'immersive', + 'utf8' + ); }); test('should change lumin icon portal package name when copying component files', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['portal.kmat', 'portal.fbx']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('com_magicscript_template').mockReturnValueOnce('com_magicscript_template'); - + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('com_magicscript_template') + .mockReturnValueOnce('com_magicscript_template'); + initUtil.copyComponentFiles('srcPath', 'dstPath', true, 'org.test.package'); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/portal.kmat'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/portal.fbx'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/portal.kmat', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/portal.fbx', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/portal.kmat', 'org_test_package', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/portal.fbx', 'org_test_package', 'utf8'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/portal.kmat', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/portal.fbx', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/portal.kmat', + 'org_test_package', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/portal.fbx', + 'org_test_package', + 'utf8' + ); }); test('should change lumin icon model package name when copying component files', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['model.kmat', 'model.fbx']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('com_magicscript_template').mockReturnValueOnce('com_magicscript_template'); - + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('com_magicscript_template') + .mockReturnValueOnce('com_magicscript_template'); + initUtil.copyComponentFiles('srcPath', 'dstPath', true, 'org.test.package'); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/model.kmat'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/model.fbx'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/model.kmat', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/model.fbx', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/model.kmat', 'org_test_package', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/model.fbx', 'org_test_package', 'utf8'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/model.kmat', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/model.fbx', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/model.kmat', + 'org_test_package', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/model.fbx', + 'org_test_package', + 'utf8' + ); }); test('should change lumin icon portal package name when copying vanilla files', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['portal.kmat', 'portal.fbx']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('com_magicleap_magicscript_hello-sample').mockReturnValueOnce('com_magicleap_magicscript_hello-sample'); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', false, 'com.test.app', 'visibleName'); + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('com_magicleap_magicscript_hello-sample') + .mockReturnValueOnce('com_magicleap_magicscript_hello-sample'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + false, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/portal.kmat'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/portal.fbx'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/portal.kmat', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/portal.fbx', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/portal.kmat', 'com_test_app', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/portal.fbx', 'com_test_app', 'utf8'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/portal.kmat', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/portal.fbx', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/portal.kmat', + 'com_test_app', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/portal.fbx', + 'com_test_app', + 'utf8' + ); }); test('should change lumin icon model package name when copying vanilla files', () => { mockedFs.existsSync.mockReturnValueOnce(true); mockedFs.readdirSync.mockReturnValueOnce(['model.kmat', 'model.fbx']); - mockedFs.statSync.mockReturnValueOnce(createStat(true, false)).mockReturnValueOnce(createStat(true, false)); - mockedFs.readFileSync.mockReturnValueOnce('com_magicleap_magicscript_hello-sample').mockReturnValueOnce('com_magicleap_magicscript_hello-sample'); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', false, 'com.test.app', 'visibleName'); + mockedFs.statSync + .mockReturnValueOnce(createStat(true, false)) + .mockReturnValueOnce(createStat(true, false)); + mockedFs.readFileSync + .mockReturnValueOnce('com_magicleap_magicscript_hello-sample') + .mockReturnValueOnce('com_magicleap_magicscript_hello-sample'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + false, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/model.kmat'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/model.fbx'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/model.kmat', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/model.fbx', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/model.kmat', 'com_test_app', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/model.fbx', 'com_test_app', 'utf8'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/model.kmat', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/model.fbx', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/model.kmat', + 'com_test_app', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/model.fbx', + 'com_test_app', + 'utf8' + ); }); test('should run recursively when its directory when copying components files', () => { mockedFs.existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true); - mockedFs.readdirSync.mockReturnValueOnce(['directory']).mockReturnValueOnce([]); + mockedFs.readdirSync + .mockReturnValueOnce(['directory']) + .mockReturnValueOnce([]); mockedFs.statSync.mockReturnValueOnce(createStat(false, true)); - + initUtil.copyComponentFiles('srcPath', 'dstPath', true, ''); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); @@ -224,19 +410,45 @@ describe('Test init utils methods', () => { .mockReturnValueOnce('srcPath/manifest.xml', 'utf8') .mockReturnValueOnce('fileContent', 'utf8') .mockReturnValueOnce('fileContent'); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', false, 'com.test.app', 'visibleName'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + false, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); - expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/manifest.xml'); + expect(mockedFs.statSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/manifest.xml' + ); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/file'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/manifest.xml', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/file', 'utf8'); + + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/manifest.xml', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/file', + 'utf8' + ); expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(3, 'srcPath/file'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/manifest.xml', 'com.test.app', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/file', 'fileContent'); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/manifest.xml', + 'com.test.app', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/file', + 'fileContent' + ); }); test('should replace app content file when is immersive', () => { @@ -249,27 +461,58 @@ describe('Test init utils methods', () => { .mockReturnValueOnce('LandscapeApp') .mockReturnValueOnce('fileContent') .mockReturnValueOnce('fileContent'); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', true, 'com.test.app', 'visibleName'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + true, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(1, 'srcPath/app.js'); expect(mockedFs.statSync).toHaveBeenNthCalledWith(2, 'srcPath/file'); - - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(1, 'srcPath/app.js', 'utf8'); - expect(mockedFs.readFileSync).toHaveBeenNthCalledWith(2, 'srcPath/file', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/app.js', 'ImmersiveApp', 'utf8'); - expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(2, 'dstPath/file', 'fileContent'); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 1, + 'srcPath/app.js', + 'utf8' + ); + expect(mockedFs.readFileSync).toHaveBeenNthCalledWith( + 2, + 'srcPath/file', + 'utf8' + ); + + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 1, + 'dstPath/app.js', + 'ImmersiveApp', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith( + 2, + 'dstPath/file', + 'fileContent' + ); // expect(mockedFs.writeFileSync).toHaveBeenNthCalledWith(1, 'dstPath/file', 'fileContent', 'utf8'); }); test('should run recursively when its directory', () => { mockedFs.existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true); - mockedFs.readdirSync.mockReturnValueOnce(['directory']).mockReturnValueOnce([]); + mockedFs.readdirSync + .mockReturnValueOnce(['directory']) + .mockReturnValueOnce([]); mockedFs.statSync.mockReturnValueOnce(createStat(false, true)); - - initUtil.copyVanillaFiles('srcPath', 'dstPath', true, 'com.test.app', 'visibleName'); + + initUtil.copyVanillaFiles( + 'srcPath', + 'dstPath', + true, + 'com.test.app', + 'visibleName' + ); expect(mockedFs.readdirSync).toHaveBeenCalledWith('srcPath'); expect(mockedFs.statSync).toHaveBeenCalledWith('srcPath/directory'); @@ -281,29 +524,35 @@ describe('Test init utils methods', () => { test('should remove files recursively for android if android is not specified', () => { let path = 'path'; mockedFs.existsSync.mockReturnValueOnce(true); - + initUtil.preparePlatforms(path, ['IOS', 'LUMIN']); - - expect(mockedFs.existsSync).toHaveBeenCalledWith('path/reactnative/android'); - expect(util.removeFilesRecursively).toHaveBeenCalledWith('path/reactnative/android'); + + expect(mockedFs.existsSync).toHaveBeenCalledWith( + 'path/reactnative/android' + ); + expect(util.removeFilesRecursively).toHaveBeenCalledWith( + 'path/reactnative/android' + ); }); test('should remove files recursively for ios if ios is not specified', () => { let path = 'path'; mockedFs.existsSync.mockReturnValueOnce(true); - + initUtil.preparePlatforms(path, ['ANDROID', 'LUMIN']); - + expect(mockedFs.existsSync).toHaveBeenCalledWith('path/reactnative/ios'); - expect(util.removeFilesRecursively).toHaveBeenCalledWith('path/reactnative/ios'); + expect(util.removeFilesRecursively).toHaveBeenCalledWith( + 'path/reactnative/ios' + ); }); test('should remove files recursively for lumin if lumin is not specified', () => { let path = 'path'; mockedFs.existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true); - + initUtil.preparePlatforms(path, ['ANDROID', 'IOS']); - + expect(mockedFs.existsSync).toHaveBeenCalledWith('path/lumin'); expect(util.removeFilesRecursively).toHaveBeenCalledWith('path/lumin'); }); @@ -311,21 +560,35 @@ describe('Test init utils methods', () => { test('should create android local properties if android is specified', () => { let path = 'path'; mockedFs.existsSync.mockReturnValueOnce(true); - + initUtil.preparePlatforms(path, ['ANDROID', 'IOS']); - - expect(mockedFs.existsSync).toHaveBeenCalledWith('path/reactnative/android/local.properties'); + + expect(mockedFs.existsSync).toHaveBeenCalledWith( + 'path/reactnative/android/local.properties' + ); }); test('should remove whole reactnative directory is neither android nor ios is specified ', () => { let path = 'path'; - mockedFs.existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true).mockReturnValueOnce(true); - + mockedFs.existsSync + .mockReturnValueOnce(true) + .mockReturnValueOnce(true) + .mockReturnValueOnce(true); + initUtil.preparePlatforms(path, ['LUMIN']); - - expect(util.removeFilesRecursively).toHaveBeenNthCalledWith(1, 'path/reactnative/ios'); - expect(util.removeFilesRecursively).toHaveBeenNthCalledWith(2, 'path/reactnative/android'); - expect(util.removeFilesRecursively).toHaveBeenNthCalledWith(3, 'path/reactnative'); + + expect(util.removeFilesRecursively).toHaveBeenNthCalledWith( + 1, + 'path/reactnative/ios' + ); + expect(util.removeFilesRecursively).toHaveBeenNthCalledWith( + 2, + 'path/reactnative/android' + ); + expect(util.removeFilesRecursively).toHaveBeenNthCalledWith( + 3, + 'path/reactnative' + ); }); test('should return true if is components option is enabled', () => { @@ -337,8 +600,12 @@ describe('Test init utils methods', () => { test('should create symlink and return true if its successful', () => { let result = initUtil.createSymlink('curDir', 'folderName'); - - expect(mockedFs.symlinkSync).toHaveBeenCalledWith('../assets', 'curDir/folderName/reactnative/assets', 'dir'); + + expect(mockedFs.symlinkSync).toHaveBeenCalledWith( + '../assets', + 'curDir/folderName/reactnative/assets', + 'dir' + ); expect(result).toBe(true); }); @@ -347,32 +614,50 @@ describe('Test init utils methods', () => { throw new Error(); }); let result = initUtil.createSymlink('curDir', 'folderName'); - - expect(mockedFs.symlinkSync).toHaveBeenCalledWith('../assets', 'curDir/folderName/reactnative/assets', 'dir'); + + expect(mockedFs.symlinkSync).toHaveBeenCalledWith( + '../assets', + 'curDir/folderName/reactnative/assets', + 'dir' + ); expect(result).toBe(false); }); test('should unlink main.js and app.js for typescript preparation', () => { initUtil.prepareTypescript('curDir', 'folder'); - - expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith(1, 'curDir/folder/src/main.js'); - expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith(2, 'curDir/folder/src/app.js'); + + expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith( + 1, + 'curDir/folder/src/main.js' + ); + expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith( + 2, + 'curDir/folder/src/app.js' + ); }); test('should unlink main.js from lumin directory and app.js from src directory for typescript components preparation', () => { initUtil.prepareComponentsTypescript('curDir', 'folder'); - - expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith(1, 'curDir/folder/lumin/src/main.js'); - expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith(2, 'curDir/folder/src/app.js'); + + expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith( + 1, + 'curDir/folder/lumin/src/main.js' + ); + expect(mockedFs.unlinkSync).toHaveBeenNthCalledWith( + 2, + 'curDir/folder/src/app.js' + ); }); test('create android local properties file if does not exist', () => { mockedFs.existsSync.mockReturnValueOnce(false); process.env.ANDROID_HOME = 'test'; - jest.spyOn(mockedFs, 'writeFileSync').mockImplementationOnce((path, content) => { - expect(path.endsWith('android/local.properties')).toBeTruthy(); - expect(content).toBe('sdk.dir=test'); - }); + jest + .spyOn(mockedFs, 'writeFileSync') + .mockImplementationOnce((path, content) => { + expect(path.endsWith('android/local.properties')).toBeTruthy(); + expect(content).toBe('sdk.dir=test'); + }); initUtil.createAndroidLocalProperties(); }); @@ -380,10 +665,12 @@ describe('Test init utils methods', () => { mockedFs.existsSync.mockReturnValueOnce(false); process.env.ANDROID_HOME = 'test'; Object.defineProperty(process, 'platform', { value: 'win32' }); - jest.spyOn(mockedFs, 'writeFileSync').mockImplementationOnce((path, content) => { - expect(path.endsWith('android/local.properties')).toBeTruthy(); - expect(content).toBe('sdk.dir=test'); - }); + jest + .spyOn(mockedFs, 'writeFileSync') + .mockImplementationOnce((path, content) => { + expect(path.endsWith('android/local.properties')).toBeTruthy(); + expect(content).toBe('sdk.dir=test'); + }); initUtil.createAndroidLocalProperties(); }); @@ -404,49 +691,84 @@ describe('Test init utils methods', () => { test('should log error when visible name is not set', () => { initUtil.renameComponentsFiles('folderName', 'packageName'); - expect(logger.red).toHaveBeenCalledWith('You have to specify the project name to rename the project!'); + expect(logger.red).toHaveBeenCalledWith( + 'You have to specify the project name to rename the project!' + ); }); test('should change visible name and package name in manifest if it exists', () => { process.cwd = jest.fn().mockReturnValue('cwd'); mockedFs.existsSync.mockReturnValueOnce(true); - mockedFs.readFileSync.mockReturnValueOnce('ml:visible_name="example" \n ml:package="com.example"'); - - initUtil.renameComponentsFiles('folderName', 'packageName', 'visibleName', ['LUMIN']); - - expect(logger.green).toHaveBeenNthCalledWith(1, 'new project name: visibleName'); - expect(logger.green).toHaveBeenNthCalledWith(2, 'Lumin manifest file has been updated successfully'); - expect(mockedFs.writeFileSync).toHaveBeenCalledWith('cwd/folderName/lumin/manifest.xml', 'ml:visible_name="visibleName" \n ml:package="packageName"', 'utf8'); + mockedFs.readFileSync.mockReturnValueOnce( + 'ml:visible_name="example" \n ml:package="com.example"' + ); + + initUtil.renameComponentsFiles('folderName', 'packageName', 'visibleName', [ + 'LUMIN' + ]); + + expect(logger.green).toHaveBeenNthCalledWith( + 1, + 'new project name: visibleName' + ); + expect(logger.green).toHaveBeenNthCalledWith( + 2, + 'Lumin manifest file has been updated successfully' + ); + expect(mockedFs.writeFileSync).toHaveBeenCalledWith( + 'cwd/folderName/lumin/manifest.xml', + 'ml:visible_name="visibleName" \n ml:package="packageName"', + 'utf8' + ); }); test('should change only visible name in manifest if package name is null', () => { process.cwd = jest.fn().mockReturnValue('cwd'); mockedFs.existsSync.mockReturnValueOnce(true); - mockedFs.readFileSync.mockReturnValueOnce('ml:visible_name="example" \n ml:package="com.example"'); - - initUtil.renameComponentsFiles('folderName', null, 'visibleName', ['LUMIN']); - - expect(logger.green).toHaveBeenNthCalledWith(1, 'new project name: visibleName'); - expect(logger.green).toHaveBeenNthCalledWith(2, 'Lumin manifest file has been updated successfully'); - expect(mockedFs.writeFileSync).toHaveBeenCalledWith('cwd/folderName/lumin/manifest.xml', 'ml:visible_name="visibleName" \n ml:package="com.example"', 'utf8'); + mockedFs.readFileSync.mockReturnValueOnce( + 'ml:visible_name="example" \n ml:package="com.example"' + ); + + initUtil.renameComponentsFiles('folderName', null, 'visibleName', [ + 'LUMIN' + ]); + + expect(logger.green).toHaveBeenNthCalledWith( + 1, + 'new project name: visibleName' + ); + expect(logger.green).toHaveBeenNthCalledWith( + 2, + 'Lumin manifest file has been updated successfully' + ); + expect(mockedFs.writeFileSync).toHaveBeenCalledWith( + 'cwd/folderName/lumin/manifest.xml', + 'ml:visible_name="visibleName" \n ml:package="com.example"', + 'utf8' + ); }); test('should rename react native files', () => { process.cwd = jest.fn().mockReturnValue('cwd'); + process.chdir = jest.fn(); mockedFs.existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true); - child_process.spawnSync.mockImplementationOnce((path, args, settings) => { - let expectedSettings = {"cwd": "cwd/folderName/reactnative", "stdio": "inherit"} - expect(args).toStrictEqual(["visibleName", "-b", "packageName"]); - expect(settings).toEqual(expect.objectContaining(expectedSettings)); - return true; - }); - initUtil.renameComponentsFiles('folderName', 'packageName', 'visibleName', ['ANDROID']); - - expect(mockedFs.existsSync).toHaveBeenNthCalledWith(1, 'cwd/folderName/lumin/manifest.xml'); - expect(mockedFs.existsSync).toHaveBeenNthCalledWith(2, 'cwd/folderName/reactnative'); - expect(logger.green).toHaveBeenCalledWith('Prepare project name and package in the project...'); - expect(child_process.spawnSync).toHaveBeenCalled(); + initUtil.renameComponentsFiles('folderName', 'packageName', 'visibleName', [ + 'ANDROID' + ]); + + expect(mockedFs.existsSync).toHaveBeenNthCalledWith( + 1, + 'cwd/folderName/lumin/manifest.xml' + ); + expect(mockedFs.existsSync).toHaveBeenNthCalledWith( + 2, + 'cwd/folderName/reactnative' + ); + expect(logger.green).toHaveBeenCalledWith( + 'Prepare project name and package in the project...' + ); + expect(mockedRename.rename).toHaveBeenCalled(); }); test('should not rename files if package or folderName does not exist', () => { @@ -454,12 +776,14 @@ describe('Test init utils methods', () => { initUtil.renameComponentsFiles(null, null, 'visibleName', ['ANDROID']); - expect(logger.yellow).toHaveBeenCalledWith('Renaming of the project could not be processed because the path does not exist!'); + expect(logger.yellow).toHaveBeenCalledWith( + 'Renaming of the project could not be processed because the path does not exist!' + ); }); test('should create repository when git is installed/configured', () => { - const pathToAppFolder = "pathToAppFolder"; - const logSuccess = "Correct log line"; + const pathToAppFolder = 'pathToAppFolder'; + const logSuccess = 'Correct log line'; child_process.execSync.mockImplementationOnce((command) => { const expectedCommand = `cd ${pathToAppFolder} && git init && git add . && git commit -am"Initial commit."`; expect(command).toStrictEqual(expectedCommand); @@ -470,8 +794,8 @@ describe('Test init utils methods', () => { }); test('should not create repository when git is not installed/configured', () => { - const pathToAppFolder = "pathToAppFolder"; - const logFailure = "Correct log line"; + const pathToAppFolder = 'pathToAppFolder'; + const logFailure = 'Correct log line'; child_process.execSync.mockImplementationOnce((command) => { const expectedCommand = `cd ${pathToAppFolder} && git init && git add . && git commit -am"Initial commit."`; expect(command).toStrictEqual(expectedCommand); @@ -481,4 +805,4 @@ describe('Test init utils methods', () => { initUtil.createGitRepository(pathToAppFolder); expect(logger.red).toHaveBeenCalledWith(logFailure); }); -}); \ No newline at end of file +}); diff --git a/__tests__/renamestepstest.js b/__tests__/renamestepstest.js new file mode 100644 index 0000000..58ad2f2 --- /dev/null +++ b/__tests__/renamestepstest.js @@ -0,0 +1,157 @@ +jest.mock('../lib/rename/renameutils.js'); +jest.mock('../lib/rename/config/bundleIdentifiers'); +jest.mock('path'); +jest.mock('fs'); +jest.mock('../lib/logger.js'); + +let { + copyFiles, + copyFileOrDir, + deleteFiles, + replaceContent +} = require('../lib/rename/renameutils.js'); +let { bundleIdentifiers } = require('../lib/rename/config/bundleIdentifiers'); +let mockedPath = require('path'); +let mockedFs = require('fs'); +let mockedLogger = require('../lib/logger'); +const renameSteps = require('../lib/rename/renamesteps'); + +afterEach(() => { + jest.resetAllMocks(); +}); + +describe('Test init utils methods', () => { + test('should resolve folders and files', () => { + const listOfFoldersAndFiles = ['file1']; + const noSpaceCurrentAppName = 'appname'; + const noSpaceNewName = 'newname'; + const dirname = 'dirname'; + mockedPath.join.mockReturnValue('path'); + + const params = { + listOfFoldersAndFiles, + noSpaceCurrentAppName, + noSpaceNewName, + dirname + }; + + renameSteps.resolveFoldersAndFiles(params); + + expect(copyFileOrDir).toHaveBeenCalledWith('path', 'path'); + expect(deleteFiles).toHaveBeenCalledWith('path'); + }); + + test('should resolve files to modify content', () => { + const regex = 'regex'; + const replacement = 'replacement'; + const paths = ['path']; + const listOfFilesToModifyContent = [{ regex, replacement, paths }]; + const bundleID = 'com.bundle.id'; + const dirname = 'dirname'; + mockedPath.join.mockReturnValue('path'); + mockedFs.existsSync.mockReturnValueOnce(true); + + const params = { + listOfFilesToModifyContent, + dirname, + bundleID + }; + + const result = renameSteps.resolveFilesToModifyContent(params); + + expect(replaceContent).toHaveBeenCalledWith(regex, replacement, ['path']); + expect(result).toBe(bundleID); + }); + + test('should resolve java files', () => { + const currentAppName = 'currName'; + const newName = 'newName'; + const newPackageName = 'com.new.name'; + const bundleID = 'com.new.name'; + const lowcaseNoSpaceNewAppName = 'newname'; + const templateBundleId = 'com.magicscript.template'; + let newJavaPath = 'com.new.name'; + const dirname = 'dirname'; + const params = { + currentAppName, + newName, + newPackageName, + bundleID, + lowcaseNoSpaceNewAppName, + templateBundleId, + newJavaPath, + dirname + }; + newJavaPath = '/android/app/src/main/java/com/new/name'; + const newBundleID = newPackageName; + + mockedPath.join.mockReturnValue('path'); + mockedFs.existsSync.mockReturnValueOnce(false); + + const result = renameSteps.resolveJavaFiles(params); + + expect(mockedFs.mkdirSync).toHaveBeenCalledWith('path', { + recursive: true + }); + expect(copyFiles).toHaveBeenCalledWith('path', 'path'); + expect(deleteFiles).toHaveBeenCalledWith('path'); + const expectedResult = { + currentAppName, + newName, + templateBundleId, + dirname, + newBundleID, + newJavaPath + }; + expect(result).toEqual(expectedResult); + }); + + test('should resolve bundle identifiers', () => { + const currentAppName = 'currName'; + const newName = 'newName'; + const templateBundleId = 'com.magicscript.template'; + const dirname = 'dirname'; + const newBundleID = 'com.new.name'; + const newJavaPath = '/android/app/src/main/java/com/new/name'; + const params = { + currentAppName, + newName, + templateBundleId, + dirname, + newBundleID, + newJavaPath + }; + + bundleIdentifiers.mockReturnValueOnce([ + { + paths: ['path'], + regex: 'regex', + replacement: 'replacement' + } + ]); + mockedPath.join.mockReturnValue('path'); + mockedFs.existsSync.mockReturnValueOnce(true); + + renameSteps.resolveBundleIdentifiers(params); + + expect(bundleIdentifiers).toHaveBeenCalledWith( + currentAppName, + newName, + templateBundleId, + newBundleID, + newJavaPath + ); + expect(replaceContent).toHaveBeenCalledWith('regex', 'replacement', [ + 'path' + ]); + }); + + test('should delete previous bundle directory', () => { + renameSteps.deletePreviousBundleDirectory('com.previous.dir'); + + expect(deleteFiles).toHaveBeenCalledWith('com/previous/dir'); + expect(mockedLogger.green).toHaveBeenCalledWith( + 'Done removing previous bundle directory.' + ); + }); +}); diff --git a/__tests__/renametest.js b/__tests__/renametest.js new file mode 100644 index 0000000..aec8955 --- /dev/null +++ b/__tests__/renametest.js @@ -0,0 +1,86 @@ +jest.mock('../lib/rename/renamesteps'); +jest.mock('../lib/rename/config/filesToModifyContent'); +jest.mock('../lib/rename/config/foldersAndFiles'); +jest.mock('fs'); +jest.mock('path'); +jest.mock('../lib/logger'); + +const renameSteps = require('../lib/rename/renamesteps'); +const configFiles = require('../lib/rename/config/filesToModifyContent'); +const foldersAndFiles = require('../lib/rename/config/foldersAndFiles'); +const mockedFs = require('fs'); +const mockedPath = require('path'); +const mockedLogger = require('../lib/logger'); +const rename = require('../lib/rename/rename'); + +afterEach(() => { + jest.resetAllMocks(); +}); + +describe('Test init utils methods', () => { + test('should rename react native files', () => { + const dirname = 'dirname'; + const newName = 'newName'; + const noSpaceNewName = newName; + const newPackageName = 'com.new.package'; + const listOfFoldersAndFiles = ['folderAndFile']; + const noSpaceCurrentAppName = 'Template'; + const listOfFilesToModifyContent = ['configFile']; + const bundleID = newPackageName; + const parameters = {}; + configFiles.filesToModifyContent.mockReturnValueOnce( + listOfFilesToModifyContent + ); + foldersAndFiles.foldersAndFiles.mockReturnValueOnce(listOfFoldersAndFiles); + renameSteps.resolveJavaFiles.mockReturnValueOnce(parameters); + mockedFs.existsSync.mockReturnValueOnce(true); + + rename.rename(dirname, newName, newPackageName); + + expect(renameSteps.resolveFoldersAndFiles).toHaveBeenCalledWith({ + listOfFoldersAndFiles, + noSpaceCurrentAppName, + noSpaceNewName, + dirname + }); + expect(renameSteps.resolveFilesToModifyContent).toHaveBeenCalledWith({ + listOfFilesToModifyContent, + dirname, + bundleID + }); + expect(renameSteps.resolveBundleIdentifiers).toHaveBeenCalledWith( + parameters + ); + expect(renameSteps.deletePreviousBundleDirectory).toHaveBeenCalledWith( + 'com.magicscript.template' + ); + expect(mockedLogger.green).toHaveBeenCalledWith( + 'APP SUCCESSFULLY RENAMED TO "newName"! 🎉 🎉 🎉' + ); + expect(mockedPath.join).toHaveBeenCalledWith(dirname, 'ios', 'Podfile'); + expect(mockedLogger.yellow).toHaveBeenCalledWith( + 'Podfile has been modified, please run "pod install" inside ios directory.' + ); + expect(mockedLogger.yellow).toHaveBeenCalledWith( + 'Please make sure to run "watchman watch-del-all" and "npm start --reset-cache" before running the app.' + ); + }); + + test('should not rename if new name is Template', () => { + let newName = 'Template'; + const newPackageName = 'com.new.package'; + const dirname = 'dirname'; + + rename.rename(dirname, newName, newPackageName); + expect(mockedLogger.red).toHaveBeenCalledWith( + 'Please try a different name.' + ); + + newName = 'template'; + + rename.rename(dirname, newName, newPackageName); + expect(mockedLogger.red).toHaveBeenCalledWith( + 'Please try a different name.' + ); + }); +}); diff --git a/__tests__/renameutilstest.js b/__tests__/renameutilstest.js new file mode 100644 index 0000000..726549d --- /dev/null +++ b/__tests__/renameutilstest.js @@ -0,0 +1,179 @@ +jest.mock('fs'); +jest.mock('../lib/logger'); +jest.mock('node-replace'); +jest.mock('path'); + +const mockedFs = require('fs'); +const renameUtils = require('../lib/rename/renameutils'); +const logger = require('../lib/logger'); +const mockedReplace = require('node-replace'); + +afterEach(() => { + jest.resetAllMocks(); +}); + +describe('Test init utils methods', () => { + function createStat(isFile) { + var statObject = {}; + statObject.isFile = function () { + return isFile; + }; + statObject.isDirectory = function () { + return !isFile; + }; + return statObject; + } + + test('should copy files with utf8', () => { + mockedFs.existsSync.mockReturnValueOnce(false); + mockedFs.readdirSync.mockReturnValueOnce(['manifest.xml', 'file']); + mockedFs.statSync + .mockReturnValueOnce(createStat(true)) + .mockReturnValueOnce(createStat(true)); + mockedFs.readFileSync + .mockReturnValueOnce('content') + .mockReturnValueOnce('fileContent'); + + renameUtils.copyFiles('srcPath', 'destPath'); + + expect(mockedFs.mkdirSync).toHaveBeenCalledWith('destPath'); + expect(mockedFs.writeFileSync).toHaveBeenCalledWith( + 'destPath/manifest.xml', + 'content', + 'utf8' + ); + expect(mockedFs.writeFileSync).toHaveBeenCalledWith( + 'destPath/file', + 'fileContent', + 'utf8' + ); + }); + + test('should recursively call copy files when is directory', () => { + mockedFs.existsSync.mockReturnValueOnce(false).mockReturnValueOnce(false); + mockedFs.readdirSync + .mockReturnValueOnce(['directory']) + .mockReturnValueOnce([]); + mockedFs.statSync.mockReturnValueOnce(createStat(false)); + mockedFs.readFileSync.mockReturnValueOnce(''); + + renameUtils.copyFiles('srcPath', 'destPath'); + + expect(mockedFs.mkdirSync).toHaveBeenCalledWith('destPath'); + expect(mockedFs.mkdirSync).toHaveBeenCalledWith('destPath/directory'); + }); + + test('should write to file when copyFileOrDir operates on file', () => { + mockedFs.statSync.mockReturnValueOnce(createStat(true)); + mockedFs.readFileSync.mockReturnValueOnce('content'); + + renameUtils.copyFileOrDir('file', 'destPath'); + + expect(mockedFs.readFileSync).toHaveBeenCalledWith('file', 'utf8'); + expect(mockedFs.writeFileSync).toHaveBeenCalledWith( + 'destPath', + 'content', + 'utf8' + ); + }); + + test('should call copyFiles when copyFileOrDir operates on directory', () => { + mockedFs.statSync.mockReturnValueOnce(createStat(false)); + mockedFs.readFileSync.mockReturnValueOnce('content'); + renameUtils.copyFiles = jest.fn(); + + renameUtils.copyFileOrDir('directory', 'destPath'); + + expect(renameUtils.copyFiles).toHaveBeenCalled(); + }); + + it('should not delete directory if bundle was not changed', () => { + const oldBundleNameDir = 'oldBundleName'; + const shouldDelete = false; + const payload = { + oldBundleNameDir, + shouldDelete + }; + + renameUtils.deletePreviousBundleDirectory(payload); + + expect(mockedFs.unlinkSync).not.toHaveBeenCalled(); + expect(logger.yellow).toHaveBeenCalledWith( + 'Bundle directory was not changed. Keeping...' + ); + }); + + it('should delete directory if bundle was changed', () => { + const oldBundleNameDir = 'oldBundleName'; + const shouldDelete = true; + const payload = { + oldBundleNameDir, + shouldDelete + }; + + renameUtils.deletePreviousBundleDirectory(payload); + + expect(mockedFs.unlinkSync).toHaveBeenCalledWith(oldBundleNameDir); + expect(logger.green).toHaveBeenCalledWith( + 'Done removing previous bundle directory.' + ); + }); + + it('should replace content', () => { + const regex = 'regex'; + const replacement = 'replacement'; + const paths = ['path1', 'path2']; + + renameUtils.replaceContent(regex, replacement, paths); + + expect(mockedReplace).toHaveBeenCalled(); + }); + + it('should unlink if is file', () => { + const path = 'path'; + mockedFs.existsSync.mockReturnValueOnce(true); + mockedFs.lstatSync.mockReturnValueOnce(createStat(true)); + + renameUtils.deleteFiles(path); + + expect(mockedFs.existsSync).toHaveBeenCalledWith(path); + expect(mockedFs.lstatSync).toHaveBeenCalledWith(path); + expect(mockedFs.unlinkSync).toHaveBeenCalledWith(path); + }); + + it('should unlink new path if is directory', () => { + const path = 'path'; + mockedFs.existsSync.mockReturnValueOnce(true); + mockedFs.lstatSync + .mockReturnValueOnce(createStat(false)) + .mockReturnValueOnce(createStat(true)); + mockedFs.readdirSync.mockReturnValueOnce(['file1']); + + renameUtils.deleteFiles(path); + + expect(mockedFs.existsSync).toHaveBeenCalledWith(path); + expect(mockedFs.lstatSync).toHaveBeenCalledWith(path); + expect(mockedFs.lstatSync).toHaveBeenCalledWith('path/file1'); + expect(mockedFs.readdirSync).toHaveBeenCalledWith(path); + expect(mockedFs.unlinkSync).toHaveBeenCalledWith('path/file1'); + expect(mockedFs.rmdirSync).toHaveBeenCalledWith(path); + }); + + it('should recurrent new path if is directory', () => { + const path = 'path'; + mockedFs.existsSync.mockReturnValueOnce(true).mockImplementationOnce(false); + mockedFs.lstatSync + .mockReturnValueOnce(createStat(false)) + .mockReturnValueOnce(createStat(false)); + mockedFs.readdirSync.mockReturnValueOnce(['innerPath']); + + renameUtils.deleteFiles(path); + + expect(mockedFs.existsSync).toHaveBeenCalledWith(path); + expect(mockedFs.lstatSync).toHaveBeenCalledWith(path); + expect(mockedFs.lstatSync).toHaveBeenCalledWith('path/innerPath'); + expect(mockedFs.readdirSync).toHaveBeenCalledWith(path); + expect(mockedFs.rmdirSync).toHaveBeenCalledWith(path); + expect(mockedFs.existsSync).toHaveBeenCalledWith('path/innerPath'); + }); +}); diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..135cd68 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,13 @@ +// babel.config.js +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current' + } + } + ] + ] +}; diff --git a/commands/build.js b/commands/build.js index 466f3f8..9b23f44 100644 --- a/commands/build.js +++ b/commands/build.js @@ -1,33 +1,235 @@ // Copyright (c) 2019 Magic Leap, Inc. All Rights Reserved // Distributed under Apache 2.0 License. See LICENSE file in the project root for full license information. -const buildUtil = require('../lib/buildutils'); -const logger = require('../lib/logger'); -const multiplatformIndexContent = "#!/system/bin/script/mxs\nimport './lumin/src/main.js';\n"; -const vanillaIndexContent = "#!/system/bin/script/mxs\nimport './src/main.js';\n"; +const { exec, spawn } = require('child_process'); +const fs = require('fs'); +const util = require('../lib/util'); + +function npmInstallIfNeeded(path, callback) { + if (fs.existsSync(`${path}/node_modules`)) { + callback(); + } else { + console.log('npm install: installing'); + + const proc = spawn('npm', ['install'], { + cwd: `${path}`, + stdio: 'inherit', + shell: process.platform === 'win32' + }); + proc.on('message', function (message) { + console.log(message); + }); + proc.on('error', function (err) { + throw err; + }); + proc.on('exit', function (code, signal) { + if (signal !== null) { + throw Error(`npm install failed with signal: ${signal}`); + } + if (code !== 0) { + throw Error(`npm install failed with code: ${code}`); + } + console.log('npm install: success'); + callback(); + }); + } +} + +function buildLuminComponents(argv) { + let path = process.cwd(); + if (fs.existsSync(`${path}/lumin`)) { + process.chdir('lumin/'); + buildLumin( + argv, + "#!/system/bin/script/mxs\nimport './lumin/src/main.js';\n" + ); + } +} + +function buildLumin(argv, indexContent) { + let packagePath = 'app.package'; + try { + for (let name of fs.readdirSync('.')) { + let m = name.match(/([^/]+)\.package$/); + if (!m) continue; + let [, base] = m; + packagePath = `${base}.package`; + } + } catch (err) { + throw err; + } + + var device = argv.debug !== false ? 'device' : 'release_device'; + var buildCommand = `mabu -t ${argv.host ? 'host' : device} ${packagePath}`; + + // create bin/index.js if needed + try { + fs.mkdirSync('bin'); + } catch (error) { + if (error.code !== 'EEXIST') { + throw error; + } + } + fs.writeFileSync('bin/index.js', indexContent, { mode: 0o755 }); + + exec('npm run build', (err, stdout, stderr) => { + if (err) { + process.stdout.write(stdout); + process.stderr.write(stderr); + throw err; + } + util.createDigest(argv.debug); + if (argv.host && fs.existsSync('app.package')) { + let packageFile = fs.openSync('app.package', 'a+'); + let buffer = fs.readFileSync(packageFile, 'utf8'); + if (buffer && !buffer.includes('USES = mxs_lumin_runtime')) { + fs.appendFileSync(packageFile, '\nUSES = mxs_lumin_runtime\n'); + } + } + exec(buildCommand, (err, stdout, stderr) => { + if (err) { + process.stdout.write(stdout); + process.stderr.write(stderr); + throw err; + } + + let mpkFile; + let outLines = stdout.split('\n'); + + let theLine = outLines.find((line) => line.includes('mpk')); + if (theLine !== undefined) { + mpkFile = theLine.substring( + theLine.indexOf("'") + 1, + theLine.lastIndexOf("'") + ); + console.log('built package: ' + mpkFile); + } else { + theLine = outLines.find((line) => line.includes('output in')); + console.log( + theLine.substring(theLine.indexOf("'"), theLine.lastIndexOf("'") + 1) + ); + } + + if (argv.install) { + argv.path = mpkFile; + util.installPackage(argv); + } + }); + }); +} module.exports = (argv) => { - if (buildUtil.isTargetSpecified(argv)) { - buildUtil.npmInstallIfNeeded(`${process.cwd()}`, () => { - if (argv.target === 'lumin') { - if (buildUtil.isMultiplatformStructure()) { - buildUtil.npmInstallIfNeeded(`${process.cwd()}/lumin`, () => { - buildUtil.navigateToLuminDirectory(); - buildUtil.buildLumin(argv, multiplatformIndexContent); - }); - } else { - buildUtil.buildLumin(argv, vanillaIndexContent); - } - } else if (buildUtil.isReactNativeTarget(argv)) { - buildUtil.npmInstallIfNeeded(`${process.cwd()}/reactnative`, () => { - if (argv.target === 'android') { - buildUtil.buildAndroid(argv); - } else if (argv.target === 'ios') { - buildUtil.buildiOS(argv); - } + npmInstallIfNeeded(`${process.cwd()}`, () => { + if (argv.target === 'lumin') { + if (isMultiplatformStructure()) { + npmInstallIfNeeded(`${process.cwd()}/lumin`, () => { + buildLuminComponents(argv); }); } + } else if (argv.target === 'android') { + npmInstallIfNeeded(`${process.cwd()}/reactnative`, () => { + buildAndroid(argv); + }); + } else if (argv.target === 'ios') { + npmInstallIfNeeded(`${process.cwd()}/reactnative`, () => { + buildiOS(argv); + }); + } else { + console.error('The target must be either lumin, ios or android!'); + } + }); +}; + +function isMultiplatformStructure() { + let path = process.cwd(); + return fs.existsSync(`${path}/lumin/rollup.config.js`); +} + +function buildAndroid() { + var path = process.cwd(); + if (fs.existsSync(`${path}/reactnative/android`)) { + fs.chmodSync(`${path}/reactnative/android/gradlew`, '755'); + var runProcess = spawn('npx', ['react-native', 'run-android'], { + stdio: 'inherit', + cwd: `${path}/reactnative`, + shell: process.platform === 'win32' + }); + runProcess.on('message', (message) => { + console.log(message); + }); + runProcess.on('error', function (err) { + throw err; + }); + runProcess.on('exit', function (code, signal) { + if (signal !== null) { + throw Error(`react-native run-android failed with signal: ${signal}`); + } + if (code !== 0) { + throw Error(`react-native run-android failed with code: ${code}`); + } + }); + } else { + console.error( + "Cannot build the app for Android because the project wasn't set up to support this platform!" + ); + } +} + +function buildiOS() { + var path = process.cwd(); + if (fs.existsSync(`${path}/reactnative/ios`)) { + installPods(path, () => { + runiOS(); }); } else { logger.red('The target must be either lumin, ios or android!'); } -}; +} + +function installPods(path, onInstallFinish) { + console.log('start installing pods'); + + var podProcess = spawn('pod', ['install'], { + stdio: 'inherit', + cwd: `${path}/reactnative/ios`, + shell: process.platform === 'win32' + }); + podProcess.on('message', function (message) { + console.log(message); + }); + podProcess.on('error', function (err) { + throw err; + }); + podProcess.on('exit', function (code, signal) { + if (signal) { + throw Error(`pod install failed with signal: ${signal}`); + } + if (code !== 0) { + throw Error(`pod install failed with code: ${code}`); + } + console.log('pod install: success'); + onInstallFinish(); + }); +} + +function runiOS() { + console.log('run ios app'); + var runProcess = spawn('npx', ['react-native', 'run-ios'], { + stdio: 'inherit', + cwd: `${process.cwd()}/reactnative`, + shell: process.platform === 'win32' + }); + runProcess.on('message', (message) => { + console.log(message); + }); + runProcess.on('error', function (err) { + throw err; + }); + runProcess.on('exit', function (code, signal) { + if (signal !== null) { + throw Error(`react-native run-ios failed with signal: ${signal}`); + } + if (code !== 0) { + throw Error(`react-native run-ios failed with code: ${code}`); + } + }); +} diff --git a/commands/init.js b/commands/init.js index b4d24d9..78314cf 100644 --- a/commands/init.js +++ b/commands/init.js @@ -97,45 +97,83 @@ module.exports = (argv) => { let destDirectory; immersive = argv.appType === 'Immersive' || argv.immersive; isComponents = argv.isComponents; - if ((typeof isComponents !== 'undefined') && isComponents === false) { - logger.green(`Start creating Vanilla Magic Script project for ${appType} app type`); + if (typeof isComponents !== 'undefined' && isComponents === false) { + logger.green( + `Start creating Vanilla Magic Script project for ${appType} app type` + ); destDirectory = path.join(currentDirectory, folderName); - initUtil.copyVanillaFiles(templatePath, destDirectory, immersive, packageName, visibleName); + initUtil.copyVanillaFiles( + templatePath, + destDirectory, + immersive, + packageName, + visibleName + ); if (argv.typeScript) { initUtil.prepareTypescript(currentDirectory, folderName); // Copy typescript template overlay over existing template files templatePath = path.join(__dirname, '../template_overlay_typescript'); - initUtil.copyVanillaFiles(templatePath, destDirectory, immersive, packageName, visibleName); + initUtil.copyVanillaFiles( + templatePath, + destDirectory, + immersive, + packageName, + visibleName + ); } - logger.green(`Vanilla Magic Script project successfully created for ${appType} app type`); + logger.green( + `Vanilla Magic Script project successfully created for ${appType} app type` + ); resetValues(); return; } - if ((typeof isComponents !== 'undefined') && isComponents === true) { + if (typeof isComponents !== 'undefined' && isComponents === true) { destDirectory = path.join(currentDirectory, folderName); setTarget(argv.target); - logger.green(`Start creating project for Components type, target: ${target}`); - templatePath = path.join(__dirname, '../template_multiplatform_components'); - initUtil.copyComponentFiles(templatePath, destDirectory, immersive, packageName); - if (argv.typeScript) { - initUtil.prepareComponentsTypescript(currentDirectory, folderName); - // Copy typescript template overlay over existing template files - templatePath = path.join(__dirname, '../template_overlay_typescript_components'); - initUtil.copyComponentFiles(templatePath, destDirectory, immersive, packageName); - } - initUtil.renameComponentsFiles(folderName, packageName, visibleName, target); - let symlinkSuccess = initUtil.createSymlink(currentDirectory, folderName); - if (!symlinkSuccess) { - logger.yellow('Couldn\'t create symlink for resources directory. Please do it manually if you want to use resources in your project. For more information check: https:///magicscript.org/'); - } - initUtil.preparePlatforms(destDirectory, target); - logger.green(`Project successfully created for platforms: ${target}`); - resetValues(); - return; + logger.green( + `Start creating project for Components type, target: ${target}` + ); + templatePath = path.join(__dirname, '../template_multiplatform_components'); + initUtil.copyComponentFiles( + templatePath, + destDirectory, + immersive, + packageName + ); + if (argv.typeScript) { + initUtil.prepareComponentsTypescript(currentDirectory, folderName); + // Copy typescript template overlay over existing template files + templatePath = path.join( + __dirname, + '../template_overlay_typescript_components' + ); + initUtil.copyComponentFiles( + templatePath, + destDirectory, + immersive, + packageName + ); + } + initUtil.renameComponentsFiles( + folderName, + packageName, + visibleName, + target + ); + let symlinkSuccess = initUtil.createSymlink(currentDirectory, folderName); + if (!symlinkSuccess) { + logger.yellow( + "Couldn't create symlink for resources directory. Please do it manually if you want to use resources in your project. For more information check: https:///magicscript.org/" + ); + } + initUtil.preparePlatforms(destDirectory, target); + logger.green(`Project successfully created for platforms: ${target}`); + resetValues(); + return; } let answerPromise = askQuestions(); answerPromise - .then(answers => { + .then((answers) => { packageName = answers['APPID']; folderName = answers['FOLDERNAME']; visibleName = answers['APPNAME']; @@ -148,37 +186,78 @@ module.exports = (argv) => { destDirectory = path.join(currentDirectory, folderName); if (isComponents) { setTarget(target); - logger.green(`Start creating project for Components type, target: ${target}`); - templatePath = path.join(__dirname, '../template_multiplatform_components'); - initUtil.copyComponentFiles(templatePath, destDirectory, immersive, packageName); + logger.green( + `Start creating project for Components type, target: ${target}` + ); + templatePath = path.join( + __dirname, + '../template_multiplatform_components' + ); + initUtil.copyComponentFiles( + templatePath, + destDirectory, + immersive, + packageName + ); if (typeScript) { initUtil.prepareComponentsTypescript(currentDirectory, folderName); // Copy typescript template overlay over existing template files - templatePath = path.join(__dirname, '../template_overlay_typescript_components'); - initUtil.copyComponentFiles(templatePath, destDirectory, immersive, packageName); + templatePath = path.join( + __dirname, + '../template_overlay_typescript_components' + ); + initUtil.copyComponentFiles( + templatePath, + destDirectory, + immersive, + packageName + ); } - initUtil.renameComponentsFiles(folderName, packageName, visibleName, target); + initUtil.renameComponentsFiles( + folderName, + packageName, + visibleName, + target + ); if (!initUtil.createSymlink(currentDirectory, folderName)) { - logger.yellow('Couldn\'t create symlink for resources directory. Please do it manually if you want to use resources in your project. For more information check: https:///magicscript.org/'); + logger.yellow( + "Couldn't create symlink for resources directory. Please do it manually if you want to use resources in your project. For more information check: https:///magicscript.org/" + ); } initUtil.preparePlatforms(destDirectory, target); logger.green(`Project successfully created for platforms: ${target}`); } else { - logger.green(`Start creating Vanilla Magic Script project for ${appType} app type`); - initUtil.copyVanillaFiles(templatePath, destDirectory, immersive, packageName, visibleName); + logger.green( + `Start creating Vanilla Magic Script project for ${appType} app type` + ); + initUtil.copyVanillaFiles( + templatePath, + destDirectory, + immersive, + packageName, + visibleName + ); if (typeScript) { initUtil.prepareTypescript(currentDirectory, folderName); // Copy typescript template overlay over existing template files templatePath = path.join(__dirname, '../template_overlay_typescript'); - initUtil.copyVanillaFiles(templatePath, destDirectory, immersive, packageName, visibleName); + initUtil.copyVanillaFiles( + templatePath, + destDirectory, + immersive, + packageName, + visibleName + ); } - logger.green(`Vanilla Magic Script project successfully created for ${appType} app type`); + logger.green( + `Vanilla Magic Script project successfully created for ${appType} app type` + ); } if (gitRepository) { initUtil.createGitRepository(folderName); } }) - .catch(err => logger.red(err)) + .catch((err) => logger.red(err)) // Add this callback for testing purpose - inquirer doesn't provide functionality to reset values // after every test and caches them .finally(() => { @@ -215,11 +294,13 @@ function setPackageName(name) { function setTarget(argTarget) { if (argTarget && Array.isArray(argTarget)) { - target = argTarget.map(string => { + target = argTarget.map((string) => { return string.toUpperCase(); }); } else { - logger.yellow('There is no proper target passed, project will generate Lumin files structure for Components app'); + logger.yellow( + 'There is no proper target passed, project will generate Lumin files structure for Components app' + ); target = ['LUMIN']; } } diff --git a/commands/install.js b/commands/install.js index 0e72e27..cc09835 100644 --- a/commands/install.js +++ b/commands/install.js @@ -3,7 +3,7 @@ const util = require('../lib/util'); const fs = require('fs'); -module.exports = argv => { +module.exports = (argv) => { util.navigateIfComponents(); let mpkPath = util.findMPKPath(); if (mpkPath && mpkPath !== '') { @@ -12,6 +12,8 @@ module.exports = argv => { if (fs.existsSync(argv.path)) { util.installPackage(argv); } else { - console.log(`MPK doesn't exist at ${argv.path}, please build it and try again.`); + console.log( + `MPK doesn't exist at ${argv.path}, please build it and try again.` + ); } }; diff --git a/commands/remove.js b/commands/remove.js index 4e5e49a..237da02 100644 --- a/commands/remove.js +++ b/commands/remove.js @@ -3,12 +3,12 @@ const { exec } = require('child_process'); const util = require('../lib/util'); -module.exports = argv => { +module.exports = (argv) => { util.navigateIfComponents(); remove(argv); }; -function remove (argv) { +function remove(argv) { let packageName = util.findPackageName(); let removeCommand = 'mldb uninstall ' + packageName; console.log(removeCommand); diff --git a/commands/run.js b/commands/run.js index 69b68d8..bb7e624 100644 --- a/commands/run.js +++ b/commands/run.js @@ -1,3 +1,4 @@ +/* eslint-disable no-inner-declarations */ // Copyright (c) 2019 Magic Leap, Inc. All Rights Reserved // Distributed under Apache 2.0 License. See LICENSE file in the project root for full license information. let { exec, spawn } = require('child_process'); @@ -10,11 +11,11 @@ let packageName; let debug; let port; -function getPortFromPackageName () { +function getPortFromPackageName() { return hash(packageName, 65535 - 1024) + 1024; } -function isRunning (callback) { +function isRunning(callback) { exec('mldb ps', (err, stdout, stderr) => { var running = null; if (err) { @@ -38,7 +39,7 @@ function isRunning (callback) { }); } -function launchFunction (callback) { +function launchFunction(callback) { let autoPrivilege = debug ? ' --auto-net-privs' : ''; let portNumber = port > 0 && port < 65536 ? port : getPortFromPackageName(); let portArg = !debug ? ' -v INSPECTOR_PORT=' + portNumber : ''; @@ -51,7 +52,7 @@ function launchFunction (callback) { return; } if (stdout.includes('Success')) { - function cb (result) { + function cb(result) { callback(result); } isRunning(cb); @@ -59,7 +60,7 @@ function launchFunction (callback) { }); } -function terminateFunction (callback) { +function terminateFunction(callback) { let launchCommand = 'mldb terminate ' + packageName; console.info('Terminating:', packageName); exec(launchCommand, (err, stdout, stderr) => { @@ -67,7 +68,7 @@ function terminateFunction (callback) { }); } -function launchCallback (pid) { +function launchCallback(pid) { if (pid == null) { console.error('Failed to launch:', packageName); return; @@ -100,7 +101,7 @@ function launchCallback (pid) { console.info(packageName, 'launched with PID:', pid); } -function runLumin (argv) { +function runLumin(argv) { let localArguments = argv._; debug = argv.debug; port = argv.port; @@ -110,18 +111,20 @@ function runLumin (argv) { packageName = util.findPackageName(); } if (packageName) { - function installedCallback (installed) { + function installedCallback(installed) { if (installed) { isRunning(runningCallback); } else { - console.warn(`Package: ${packageName} is not installed. Please install it.`); + console.warn( + `Package: ${packageName} is not installed. Please install it.` + ); } } - function runningCallback (pid) { + function runningCallback(pid) { if (pid == null) { launchFunction(launchCallback); } else { - function launchMe () { + function launchMe() { launchFunction(launchCallback); } terminateFunction(launchMe); @@ -131,7 +134,7 @@ function runLumin (argv) { } } -module.exports = argv => { +module.exports = (argv) => { if (argv.target === 'lumin') { util.navigateIfComponents(); runLumin(argv); diff --git a/jest.config.js b/jest.config.js index b39b33a..ea3d47b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,6 +10,7 @@ module.exports = { collectCoverageFrom: [ 'commands/*.js', 'lib/*.js', + 'lib/rename/*.js', 'util/*.js' ], testEnvironment: 'node', diff --git a/lib/initutils.js b/lib/initutils.js index ec9b575..bde9828 100644 --- a/lib/initutils.js +++ b/lib/initutils.js @@ -3,8 +3,9 @@ let fs = require('fs'); const logger = require('../lib/logger'); const util = require('../lib/util'); -const rename = require.resolve('react-native-rename'); -let { spawnSync, execSync } = require('child_process'); +// const rename = require.resolve('react-native-rename'); +let { rename } = require('../lib/rename/rename'); +let { execSync } = require('child_process'); module.exports.updateManifest = function ( contents, @@ -14,7 +15,7 @@ module.exports.updateManifest = function ( ) { logger.yellow('Updating manifest file'); let replaced = contents - .replace('com.magicleap.magicscript.hello-sample', packageName) + .replace('com.magicleap.magicscript.hello-sample', packageName) .replace(new RegExp('MagicScript Hello Sample', 'g'), visibleName); if (immersive) { replaced = replaced @@ -29,37 +30,51 @@ module.exports.updateManifest = function ( }; module.exports.updateComponentManifest = function (contents) { - let replaced = contents.replace('universe', 'fullscreen') - .replace('Universe', 'Fullscreen') - .replace( - '', - '\n ' - ); - return replaced + let replaced = contents + .replace('universe', 'fullscreen') + .replace('Universe', 'Fullscreen') + .replace( + '', + '\n ' + ); + return replaced; }; -module.exports.copyComponentFiles = function (srcPath, destPath, immersive, packageName) { +module.exports.copyComponentFiles = function ( + srcPath, + destPath, + immersive, + packageName +) { if (!fs.existsSync(destPath)) { fs.mkdirSync(destPath); } const filesToCreate = fs.readdirSync(srcPath); - filesToCreate.forEach(file => { + filesToCreate.forEach((file) => { const origFilePath = `${srcPath}/${file}`; const stats = fs.statSync(origFilePath); if (stats.isFile()) { - if(immersive && file === 'manifest.xml') { + if (immersive && file === 'manifest.xml') { var contents = fs.readFileSync(origFilePath, 'utf8'); contents = this.updateComponentManifest(contents); const writePath = `${destPath}/${file}`; fs.writeFileSync(writePath, contents, 'utf8'); - } else if(immersive && (file === 'main.js' || file === 'main.tsx')) { + } else if (immersive && (file === 'main.js' || file === 'main.tsx')) { var contents = fs.readFileSync(origFilePath, 'utf8'); contents = contents.replace('landscape', 'immersive'); const writePath = `${destPath}/${file}`; fs.writeFileSync(writePath, contents, 'utf8'); - } else if (file === 'model.fbx' || file === 'model.kmat' || file === 'portal.fbx' || file === 'portal.kmat') { + } else if ( + file === 'model.fbx' || + file === 'model.kmat' || + file === 'portal.fbx' || + file === 'portal.kmat' + ) { var contents = fs.readFileSync(origFilePath, 'utf8'); - contents = contents.replace(new RegExp('com_magicscript_template', 'g'), packageName.replace(/\./g, '_')); + contents = contents.replace( + new RegExp('com_magicscript_template', 'g'), + packageName.replace(/\./g, '_') + ); const writePath = `${destPath}/${file}`; fs.writeFileSync(writePath, contents, 'utf8'); } else { @@ -69,36 +84,69 @@ module.exports.copyComponentFiles = function (srcPath, destPath, immersive, pack } } else if (stats.isDirectory()) { let newDestPath = `${destPath}/${file}`; - this.copyComponentFiles(origFilePath, newDestPath, immersive, packageName); + this.copyComponentFiles( + origFilePath, + newDestPath, + immersive, + packageName + ); } }); -} +}; -module.exports.copyVanillaFiles = function (srcPath, destPath, immersive, packageName, visibleName) { +module.exports.copyVanillaFiles = function ( + srcPath, + destPath, + immersive, + packageName, + visibleName +) { if (!fs.existsSync(destPath)) { fs.mkdirSync(destPath); } const filesToCreate = fs.readdirSync(srcPath); - filesToCreate.forEach(file => { + filesToCreate.forEach((file) => { const origFilePath = `${srcPath}/${file}`; const stats = fs.statSync(origFilePath); if (stats.isFile()) { var contents = fs.readFileSync(origFilePath, 'utf8'); if (file === 'manifest.xml') { - contents = this.updateManifest(contents, packageName, visibleName, immersive); + contents = this.updateManifest( + contents, + packageName, + visibleName, + immersive + ); fs.writeFileSync(`${destPath}/${file}`, contents, 'utf8'); } else if (immersive && (file === 'app.js' || file === 'app.ts')) { - contents = contents.replace(new RegExp('LandscapeApp', 'g'), 'ImmersiveApp'); + contents = contents.replace( + new RegExp('LandscapeApp', 'g'), + 'ImmersiveApp' + ); fs.writeFileSync(`${destPath}/${file}`, contents, 'utf8'); - } else if (file === 'model.fbx' || file === 'model.kmat' || file === 'portal.fbx' || file === 'portal.kmat') { - contents = contents.replace(new RegExp('com_magicleap_magicscript_hello-sample', 'g'), packageName.replace(/\./g, '_')); + } else if ( + file === 'model.fbx' || + file === 'model.kmat' || + file === 'portal.fbx' || + file === 'portal.kmat' + ) { + contents = contents.replace( + new RegExp('com_magicleap_magicscript_hello-sample', 'g'), + packageName.replace(/\./g, '_') + ); fs.writeFileSync(`${destPath}/${file}`, contents, 'utf8'); } else { fs.writeFileSync(`${destPath}/${file}`, fs.readFileSync(origFilePath)); } } else if (stats.isDirectory()) { let newDestPath = `${destPath}/${file}`; - this.copyVanillaFiles(origFilePath, newDestPath, immersive, packageName, visibleName); + this.copyVanillaFiles( + origFilePath, + newDestPath, + immersive, + packageName, + visibleName + ); } }); }; @@ -132,36 +180,45 @@ module.exports.preparePlatforms = function (destPath, target) { } }; - module.exports.isComponentsAppType = function (answers) { return answers.ISCOMPONENTS === true; }; module.exports.createSymlink = function (currentDirectory, folderName) { try { - fs.symlinkSync(`../assets`, `${currentDirectory}/${folderName}/reactnative/assets`, 'dir'); + fs.symlinkSync( + `../assets`, + `${currentDirectory}/${folderName}/reactnative/assets`, + 'dir' + ); return true; } catch (error) { return false; } }; -module.exports.prepareComponentsTypescript = function(currentDirectory, folderName) { +module.exports.prepareComponentsTypescript = function ( + currentDirectory, + folderName +) { fs.unlinkSync(`${currentDirectory}/${folderName}/lumin/src/main.js`); fs.unlinkSync(`${currentDirectory}/${folderName}/src/app.js`); -} +}; module.exports.prepareTypescript = function (currentDirectory, folderName) { fs.unlinkSync(`${currentDirectory}/${folderName}/src/main.js`); fs.unlinkSync(`${currentDirectory}/${folderName}/src/app.js`); }; -module.exports.renameComponentsFiles = function (folderName, packageName, visibleName, target) { +module.exports.renameComponentsFiles = function ( + folderName, + packageName, + visibleName, + target +) { let projectPath = process.cwd(); if (!visibleName) { - logger.red( - 'You have to specify the project name to rename the project!' - ); + logger.red('You have to specify the project name to rename the project!'); } else { let manifestPath = `${projectPath}/${folderName}/lumin/manifest.xml`; if (fs.existsSync(manifestPath)) { @@ -192,19 +249,12 @@ module.exports.renameComponentsFiles = function (folderName, packageName, visibl } } let isReact = target.includes('IOS') || target.includes('ANDROID'); - if(isReact) { - var renameCommand = rename; - if (process.platform === 'win32') { - renameCommand = `node ${rename}`; - } + if (isReact) { var reactNativeProjectPath = `${process.cwd()}/${folderName}/reactnative`; if (fs.existsSync(reactNativeProjectPath)) { logger.green('Prepare project name and package in the project...'); - spawnSync(renameCommand, [`${visibleName}`, '-b', `${packageName}`], { - cwd: `${reactNativeProjectPath}`, - shell: process.platform === 'win32', - stdio: 'inherit' - }); + process.chdir(reactNativeProjectPath); + rename(reactNativeProjectPath, folderName, packageName); } else { logger.yellow( 'Renaming of the project could not be processed because the path does not exist!' @@ -212,7 +262,7 @@ module.exports.renameComponentsFiles = function (folderName, packageName, visibl } } } -} +}; module.exports.createAndroidLocalProperties = function (projPath) { if (!fs.existsSync(`${projPath}/reactnative/android/local.properties`)) { @@ -222,7 +272,11 @@ module.exports.createAndroidLocalProperties = function (projPath) { androidHome = androidHome.replace(/\\/g, '\\\\'); } let sdkDir = 'sdk.dir=' + androidHome; - fs.writeFileSync(`${projPath}/reactnative/android/local.properties`, sdkDir, 'utf-8'); + fs.writeFileSync( + `${projPath}/reactnative/android/local.properties`, + sdkDir, + 'utf-8' + ); logger.green('Successfully created local.properties file!'); } else { logger.yellow( @@ -236,9 +290,11 @@ module.exports.createAndroidLocalProperties = function (projPath) { module.exports.createGitRepository = function (repositoryPath) { try { - execSync(`cd ${repositoryPath} && git init && git add . && git commit -am"Initial commit."`); - logger.green("git repository initialised."); + execSync( + `cd ${repositoryPath} && git init && git add . && git commit -am"Initial commit."` + ); + logger.green('git repository initialised.'); } catch (error) { - logger.red("git repository not initialised - you have to do it manually."); + logger.red('git repository not initialised - you have to do it manually.'); } }; diff --git a/lib/logger.js b/lib/logger.js index 531d472..8b5431b 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -4,17 +4,17 @@ const yellow = '\x1b[33m'; const normal = '\x1b[0m'; module.exports.red = function (text) { - console.log(red, text); + console.log(red, text); }; module.exports.green = function (text) { - console.log(green, text); + console.log(green, text); }; module.exports.yellow = function (text) { - console.log(yellow, text); + console.log(yellow, text); }; module.exports.normal = function (text) { - console.log(normal, text); -}; \ No newline at end of file + console.log(normal, text); +}; diff --git a/lib/rename/config/bundleIdentifiers.js b/lib/rename/config/bundleIdentifiers.js new file mode 100644 index 0000000..27fccec --- /dev/null +++ b/lib/rename/config/bundleIdentifiers.js @@ -0,0 +1,57 @@ +// nS - No Space +// lC - Lowercase +let path = require('path'); + +module.exports.bundleIdentifiers = function ( + currentAppName, + newName, + currentBundleID, + newBundleID, + newBundlePath +) { + const noSpaceCurrentAppName = currentAppName.replace(/\s/g, ''); + const noSpaceNewName = newName.replace(/\s/g, ''); + const lowcaseNoSpaceCurrentBundleID = currentBundleID.toLowerCase(); + const lowcaseNoSpaceNewBundleID = newBundleID.toLowerCase(); + + return [ + { + regex: currentBundleID, + replacement: newBundleID, + paths: [ + 'android/app/BUCK', + 'android/app/build.gradle', + 'android/app/src/main/AndroidManifest.xml' + ] + }, + { + regex: currentBundleID, + replacement: newBundleID, + paths: [ + path.join(`${newBundlePath}`, 'MainActivity.java'), + path.join(`${newBundlePath}`, 'MainApplication.java') + ] + }, + { + regex: lowcaseNoSpaceCurrentBundleID, + replacement: lowcaseNoSpaceNewBundleID, + paths: [path.join(`${newBundlePath}`, 'MainApplication.java')] + }, + { + // App name (probably) doesn't start with `.`, but the bundle ID will + // include the `.`. This fixes a possible issue where the bundle ID + // also contains the app name and prevents it from being inappropriately + // replaced by an update to the app name with the same bundle ID + regex: new RegExp(`(?!\\.)(.|^)${noSpaceCurrentAppName}`, 'g'), + replacement: `$1${noSpaceNewName}`, + paths: [path.join(`${newBundlePath}`, 'MainActivity.java')] + }, + { + regex: currentBundleID, + replacement: newBundleID, + paths: [ + path.join('ios', `${noSpaceNewName}.xcodeproj`, 'project.pbxproj') + ] + } + ]; +}; diff --git a/lib/rename/config/filesToModifyContent.js b/lib/rename/config/filesToModifyContent.js new file mode 100644 index 0000000..a48d599 --- /dev/null +++ b/lib/rename/config/filesToModifyContent.js @@ -0,0 +1,66 @@ +// nS - No Space +// lC - Lowercase +let path = require('path'); + +module.exports.filesToModifyContent = function (currentAppName, newName) { + const noSpaceCurrentAppName = currentAppName.replace(/\s/g, ''); + const noSpaceNewName = newName.replace(/\s/g, ''); + + return [ + { + regex: `${currentAppName}`, + replacement: `${newName}`, + paths: ['android/app/src/main/res/values/strings.xml'] + }, + { + regex: noSpaceCurrentAppName, + replacement: noSpaceNewName, + paths: [ + 'index.js', + 'index.android.js', + 'index.ios.js', + path.join('ios', `${noSpaceNewName}.xcodeproj`, 'project.pbxproj'), + path.join( + 'ios', + `${noSpaceNewName}.xcworkspace`, + 'contents.xcworkspacedata' + ), + path.join( + 'ios', + `${noSpaceNewName}.xcodeproj`, + 'xcshareddata', + 'xcschemes', + `${noSpaceNewName}.xcscheme` + ), + path.join('ios', `${noSpaceNewName}`, 'AppDelegate.m'), + path.join('android', 'settings.gradle'), + path.join('ios', `${noSpaceNewName}Tests`, `${noSpaceNewName}Tests.m`), + path.join('ios', 'build', 'info.plist'), + path.join('ios', 'Podfile'), + 'app.json' + ] + }, + { + regex: `text="${currentAppName}"`, + replacement: `text="${newName}"`, + paths: [ + path.join('ios', `${noSpaceNewName}`, 'Base.lproj', 'LaunchScreen.xib') + ] + }, + { + regex: currentAppName, + replacement: newName, + paths: [path.join('ios', `${noSpaceNewName}`, 'Info.plist')] + }, + { + regex: `"name": "${noSpaceCurrentAppName}"`, + replacement: `"name": "${noSpaceNewName}"`, + paths: ['package.json'] + }, + { + regex: `"displayName": "${currentAppName}"`, + replacement: `"displayName": "${newName}"`, + paths: ['app.json'] + } + ]; +}; diff --git a/lib/rename/config/foldersAndFiles.js b/lib/rename/config/foldersAndFiles.js new file mode 100644 index 0000000..9e2e39e --- /dev/null +++ b/lib/rename/config/foldersAndFiles.js @@ -0,0 +1,14 @@ +module.exports.foldersAndFiles = function (currentAppName, newName) { + const noSpaceCurrentAppName = currentAppName.replace(/\s/g, ''); + const noSpaceNewName = newName.replace(/\s/g, ''); + + return [ + `ios/${noSpaceCurrentAppName}`, + `ios/${noSpaceCurrentAppName}.xcodeproj`, + `ios/${noSpaceNewName}.xcodeproj/xcshareddata/xcschemes/${noSpaceCurrentAppName}.xcscheme`, + `ios/${noSpaceCurrentAppName}Tests`, + `ios/${noSpaceNewName}Tests/${noSpaceCurrentAppName}Tests.m`, + `ios/${noSpaceCurrentAppName}.xcworkspace`, + `ios/${noSpaceCurrentAppName}-Bridging-Header.h` + ]; +}; diff --git a/lib/rename/rename.js b/lib/rename/rename.js new file mode 100644 index 0000000..d0f5971 --- /dev/null +++ b/lib/rename/rename.js @@ -0,0 +1,68 @@ +let { + resolveFoldersAndFiles, + resolveFilesToModifyContent, + resolveJavaFiles, + deletePreviousBundleDirectory, + resolveBundleIdentifiers +} = require('./renamesteps'); +let { filesToModifyContent } = require('./config/filesToModifyContent'); +let { foldersAndFiles } = require('./config/foldersAndFiles'); +let fs = require('fs'); +let path = require('path'); +let logger = require('../logger'); +const templateAppName = 'Template'; +const templateBundleId = 'com.magicscript.template'; + +// We are not validating name or package name at this point - it is validated when passing data via CLI in the first place. +module.exports.rename = function (dirname, newName, newPackageName) { + const currentAppName = templateAppName; + const noSpaceCurrentAppName = templateAppName; + const lowcaseNoSpaceCurrentAppName = templateAppName.toLowerCase(); + const noSpaceNewName = newName.replace(/\s/g, ''); + const lowcaseNoSpaceNewAppName = noSpaceNewName.toLowerCase(); + const bundleID = newPackageName.toLowerCase(); + const listOfFoldersAndFiles = foldersAndFiles(currentAppName, newName); + const listOfFilesToModifyContent = filesToModifyContent( + currentAppName, + newName + ); + + if ( + newName === currentAppName || + newName === noSpaceCurrentAppName || + newName === lowcaseNoSpaceCurrentAppName + ) { + return logger.red('Please try a different name.'); + } + resolveFoldersAndFiles({ + listOfFoldersAndFiles, + noSpaceCurrentAppName, + noSpaceNewName, + dirname + }); + resolveFilesToModifyContent({ + listOfFilesToModifyContent, + dirname, + bundleID + }); + const parameters = resolveJavaFiles({ + currentAppName, + newName, + newPackageName, + bundleID, + lowcaseNoSpaceNewAppName, + templateBundleId, + dirname + }); + resolveBundleIdentifiers(parameters); + deletePreviousBundleDirectory(templateBundleId); + logger.green(`APP SUCCESSFULLY RENAMED TO "${newName}"! 🎉 🎉 🎉`); + if (fs.existsSync(path.join(dirname, 'ios', 'Podfile'))) { + logger.yellow( + 'Podfile has been modified, please run "pod install" inside ios directory.' + ); + } + logger.yellow( + 'Please make sure to run "watchman watch-del-all" and "npm start --reset-cache" before running the app.' + ); +}; diff --git a/lib/rename/renamesteps.js b/lib/rename/renamesteps.js new file mode 100644 index 0000000..6b0ddd1 --- /dev/null +++ b/lib/rename/renamesteps.js @@ -0,0 +1,111 @@ +let { + copyFiles, + copyFileOrDir, + deleteFiles, + replaceContent +} = require('./renameutils'); +let { bundleIdentifiers } = require('./config/bundleIdentifiers'); +let path = require('path'); +let fs = require('fs'); +let logger = require('../logger'); + +module.exports.resolveFoldersAndFiles = function (params) { + const { + listOfFoldersAndFiles, + noSpaceCurrentAppName, + noSpaceNewName, + dirname + } = params; + listOfFoldersAndFiles.forEach((element, index) => { + const dest = element.replace( + new RegExp(noSpaceCurrentAppName, 'i'), + noSpaceNewName + ); + copyFileOrDir(path.join(dirname, element), path.join(dirname, dest)); + deleteFiles(path.join(dirname, element)); + }); +}; + +module.exports.resolveFilesToModifyContent = function (params) { + const { listOfFilesToModifyContent, dirname, bundleID } = params; + listOfFilesToModifyContent.map((file) => { + file.paths.map((filePath, index) => { + const newPaths = []; + if (fs.existsSync(path.join(dirname, filePath))) { + newPaths.push(path.join(dirname, filePath)); + replaceContent(file.regex, file.replacement, newPaths); + } + }); + }); + return bundleID; +}; + +module.exports.resolveJavaFiles = function (params) { + let { + currentAppName, + newName, + newPackageName, + bundleID, + lowcaseNoSpaceNewAppName, + templateBundleId, + dirname + } = params; + const newBundleID = bundleID; + const javaFileBase = '/android/app/src/main/java'; + const newJavaPath = `${javaFileBase}/${newPackageName.replace(/\./g, '/')}`; + const currentJavaPath = `${javaFileBase}/${templateBundleId.replace( + /\./g, + '/' + )}`; + + const fullCurrentBundlePath = path.join(dirname, currentJavaPath); + const fullNewBundlePath = path.join(dirname, newJavaPath); + + // Create new bundle folder if doesn't exist yet + if (!fs.existsSync(fullNewBundlePath)) { + fs.mkdirSync(fullNewBundlePath, { recursive: true }); + copyFiles(fullCurrentBundlePath, fullNewBundlePath); + deleteFiles(fullCurrentBundlePath); + } + + return { + currentAppName, + newName, + templateBundleId, + dirname, + newBundleID, + newJavaPath + }; +}; + +module.exports.deletePreviousBundleDirectory = function (templateBundleId) { + const dir = templateBundleId.replace(/\./g, '/'); + deleteFiles(dir); + logger.green('Done removing previous bundle directory.'); +}; + +module.exports.resolveBundleIdentifiers = function (params) { + const { + currentAppName, + newName, + templateBundleId, + dirname, + newBundleID, + newJavaPath + } = params; + bundleIdentifiers( + currentAppName, + newName, + templateBundleId, + newBundleID, + newJavaPath + ).map((file) => { + file.paths.map((filePath, index) => { + const newPaths = []; + if (fs.existsSync(path.join(dirname, filePath))) { + newPaths.push(path.join(dirname, filePath)); + replaceContent(file.regex, file.replacement, newPaths); + } + }); + }); +}; diff --git a/lib/rename/renameutils.js b/lib/rename/renameutils.js new file mode 100644 index 0000000..db9b28a --- /dev/null +++ b/lib/rename/renameutils.js @@ -0,0 +1,79 @@ +let fs = require('fs'); +let logger = require('../logger'); +let replace = require('node-replace'); + +const replaceOptions = { + recursive: true, + silent: true +}; + +module.exports.copyFiles = function (srcPath, destPath) { + if (!fs.existsSync(destPath)) { + fs.mkdirSync(destPath); + } + const filesToCreate = fs.readdirSync(srcPath); + filesToCreate.forEach((file) => { + const origFilePath = `${srcPath}/${file}`; + const stats = fs.statSync(origFilePath); + if (stats.isFile()) { + var contents = fs.readFileSync(origFilePath, 'utf8'); + const writePath = `${destPath}/${file}`; + fs.writeFileSync(writePath, contents, 'utf8'); + } else if (stats.isDirectory()) { + let newDestPath = `${destPath}/${file}`; + module.exports.copyFiles(origFilePath, newDestPath); + } + }); +}; + +module.exports.copyFileOrDir = function (element, destPath) { + const stats = fs.statSync(element); + if (stats.isFile()) { + var contents = fs.readFileSync(element, 'utf8'); + fs.writeFileSync(destPath, contents, 'utf8'); + } else if (stats.isDirectory()) { + module.exports.copyFiles(element, destPath); + } +}; + +module.exports.deletePreviousBundleDirectory = function ({ + oldBundleNameDir, + shouldDelete +}) { + if (shouldDelete) { + const dir = oldBundleNameDir.replace(/\./g, '/'); + fs.unlinkSync(dir); + logger.green('Done removing previous bundle directory.'); + } else { + logger.yellow('Bundle directory was not changed. Keeping...'); + } +}; + +module.exports.replaceContent = function (regex, replacement, paths) { + replace({ + regex, + replacement, + paths, + ...replaceOptions + }); +}; + +module.exports.deleteFiles = function (path) { + var files = []; + if (fs.existsSync(path)) { + if (fs.lstatSync(path).isFile()) { + fs.unlinkSync(path); + } else { + files = fs.readdirSync(path); + files.forEach(function (file, index) { + var curPath = path + '/' + file; + if (fs.lstatSync(curPath).isDirectory()) { + module.exports.deleteFiles(curPath); + } else { + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } + } +}; diff --git a/lib/util.js b/lib/util.js index 45b1add..900df57 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2,9 +2,8 @@ // Distributed under Apache 2.0 License. See LICENSE file in the project root for full license information. let fs = require('fs'); let glob = require('glob'); -let { exec, execFile, execSync, spawnSync, spawn } = require('child_process'); +let { exec, execFile, execSync, spawn } = require('child_process'); const path = require('path'); -const rename = require.resolve('react-native-rename'); module.exports.findPackageName = function () { let manifestPath = 'manifest.xml'; @@ -80,7 +79,7 @@ module.exports.installPackage = function (argv) { if (argv.target === 'lumin') { let packageName = this.findPackageName(); navigateIfComponents(); - this.isInstalled(packageName, installed => { + this.isInstalled(packageName, (installed) => { var args = ['install']; if (installed) { args.push('-u'); @@ -91,13 +90,13 @@ module.exports.installPackage = function (argv) { shell: process.platform === 'win32' }); if (child) { - child.stdout.on('data', data => { + child.stdout.on('data', (data) => { process.stdout.write(data); }); - child.stderr.on('data', data => { + child.stderr.on('data', (data) => { process.stderr.write(data); }); - child.on('error', err => { + child.on('error', (err) => { throw err; }); } @@ -144,8 +143,7 @@ module.exports.validateFolderName = function (folderName) { module.exports.validateAppName = function (appName) { if (!module.exports.isValidAppName(appName)) { return ( - 'Invalid app name. Must match ' + - module.exports.AppNameRegex.toString() + 'Invalid app name. Must match ' + module.exports.AppNameRegex.toString() ); } return true; @@ -158,7 +156,7 @@ module.exports.isValidAppType = function (appType) { module.exports.navigateIfComponents = navigateIfComponents; -function navigateIfComponents () { +function navigateIfComponents() { const path = process.cwd(); if (fs.existsSync(`${path}/lumin`)) { process.chdir('lumin'); @@ -167,9 +165,9 @@ function navigateIfComponents () { module.exports.removeFilesRecursively = removeFilesRecursively; -function removeFilesRecursively (path) { +function removeFilesRecursively(path) { const filesToDelete = fs.readdirSync(path); - filesToDelete.forEach(file => { + filesToDelete.forEach((file) => { const curPath = `${path}/${file}`; if (fs.lstatSync(curPath).isDirectory()) { removeFilesRecursively(curPath); @@ -186,7 +184,8 @@ module.exports.findMPKPath = function () { if (!fs.existsSync(packagePath)) { console.error('Cannot file: app.package'); } else { - var output = '' + execSync('mabu -t device --print-package-outputs app.package'); + var output = + '' + execSync('mabu -t device --print-package-outputs app.package'); let tokenized = output.split('\t'); if (tokenized && tokenized.length > 1) { mpkName = tokenized[1].trim(); diff --git a/package.json b/package.json index eacaf53..af56ca5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "glob": "^7.1.3", "hash-index": "^3.0.0", "inquirer": "^6.3.1", + "node-replace": "^0.3.3", "react-native-rename": "github:magic-script/react-native-rename", "which": "^1.3.1", "yargs": "^12.0.2" @@ -24,6 +25,9 @@ "testEnvironment": "node" }, "devDependencies": { + "@babel/core": "^7.10.2", + "@babel/preset-env": "^7.10.2", + "babel-jest": "^26.0.1", "eslint": "^5.12.1", "eslint-config-semistandard": "^13.0.0", "eslint-config-standard": "^12.0.0", @@ -32,7 +36,7 @@ "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", - "jest": "^24.8.0", + "jest": "^26.0.1", "semistandard": "^13.0.1" }, "engines": { @@ -41,6 +45,7 @@ "files": [ "/bin", "/lib", + "/lib/rename", "/commands", "/template", "/template_components",