diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a962f35be..03d5ac0faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ Our versioning strategy is as follows: * XMCloud-based: 'angular,angular-xmcloud' * Rework Angular initializer to support XMCloud and SXP journeys; +* `[create-sitecore-jss]` Rework Angular initializer to support XMCloud and SXP journeys ([#1845](https://github.com/Sitecore/jss/pull/1845))([#1858](https://github.com/Sitecore/jss/pull/1858)) +* `[create-sitecore-jss]` Allows proxy apps to be installed alongside main apps ([#1858](https://github.com/Sitecore/jss/pull/1858)) + * `nodeAppDestination` arg can be passed into `create-sitecore-jss` command to define path for proxy to be installed in + ### ๐Ÿ›  Breaking Change ### ๐Ÿงน Chores diff --git a/packages/create-sitecore-jss/src/bin.test.ts b/packages/create-sitecore-jss/src/bin.test.ts index 09fe4d1ea8..946748b019 100644 --- a/packages/create-sitecore-jss/src/bin.test.ts +++ b/packages/create-sitecore-jss/src/bin.test.ts @@ -6,7 +6,7 @@ import { sep } from 'path'; import chalk from 'chalk'; import inquirer from 'inquirer'; import { ParsedArgs } from 'minimist'; -import { parseArgs, main } from './bin'; +import { parseArgs, main, promptDestination, getDestinations } from './bin'; import * as helpers from './common/utils/helpers'; import * as initRunner from './init-runner'; @@ -37,6 +37,8 @@ describe('bin', () => { 'test', '--destination', '.\\test\\path', + '--proxyAppDestination', + '.\\test\\proxypath', '--templates', 'foo,bar', '--hostName', @@ -231,34 +233,6 @@ describe('bin', () => { }); }); - it('should prompt for destination if missing', async () => { - getAllTemplatesStub.returns(['foo', 'bar']); - getBaseTemplatesStub.returns(['foo']); - fsExistsSyncStub.returns(false); - fsReaddirSyncStub.returns([]); - inquirerPromptStub.returns({ - prePushHook: true, - }); - - const mockDestination = 'my\\path'; - inquirerPromptStub.returns({ - destination: mockDestination, - }); - - const args = mockArgs({ - templates: 'foo', - }); - const expectedTemplates = ['foo']; - await main(args); - - expect(inquirerPromptStub).to.have.been.called; - expect(initRunnerStub).to.have.been.calledWith(expectedTemplates, { - ...args, - destination: mockDestination, - templates: expectedTemplates, - }); - }); - it('should prompt for prePushHook if missing', async () => { getAllTemplatesStub.returns(['foo', 'bar']); getBaseTemplatesStub.returns(['foo']); @@ -287,88 +261,189 @@ describe('bin', () => { }); }); - describe('destination default', () => { - it('should use appName', async () => { - getAllTemplatesStub.returns(['foo', 'bar']); - getBaseTemplatesStub.returns(['foo']); - fsExistsSyncStub.returns(false); - fsReaddirSyncStub.returns([]); - + describe('promptDestination', () => { + it('should prompt with provided prompt text and return input value', async () => { const mockDestination = 'my\\path'; + const mockPrompt = 'Enter the mocking path'; inquirerPromptStub.returns({ destination: mockDestination, }); + const result = await promptDestination(mockPrompt, 'default'); + expect(inquirerPromptStub).to.have.been.called; + expect(inquirerPromptStub.getCall(0).args[0].message).to.be.equal(mockPrompt); + expect(result).to.be.equal(mockDestination); + }); - const args = mockArgs({ - templates: 'foo', - appName: 'testApp', + it('should use default value', async () => { + inquirerPromptStub.returns({ + destination: undefined, }); - const expectedTemplates = ['foo']; - const expectedDestinationDefault = `${process.cwd()}${sep + args.appName}`; + const defaultDestination = 'defa\\ult'; + await promptDestination('use default here', defaultDestination); + expect(inquirerPromptStub).to.have.been.called; + expect(inquirerPromptStub.getCall(0).args[0].default()).to.be.equal(defaultDestination); + }); + }); - await main(args); + describe('getDestinations', () => { + const testTemplates = ['foo', 'bar']; - expect(inquirerPromptStub).to.have.been.called; - expect(inquirerPromptStub.getCall(0).args[0].default()).to.equal( - expectedDestinationDefault - ); - expect(initRunnerStub).to.have.been.calledWith(expectedTemplates, { - ...args, - destination: mockDestination, - templates: expectedTemplates, + it('should return base args.destination value only when provided', async () => { + const testPath = 'test\\path'; + const testArgs = mockArgs({ + destination: testPath, + }); + expect(await getDestinations(testArgs, testTemplates)).to.deep.equal({ + destination: testPath, }); }); - it('should use template if appName not provided', async () => { - getAllTemplatesStub.returns(['foo', 'bar']); - getBaseTemplatesStub.returns(['foo']); - fsExistsSyncStub.returns(false); - fsReaddirSyncStub.returns([]); + it('should return base and proxy destinations from args when templates contain proxy app', async () => { + const testPath = 'test\\path'; + const proxyPath = 'proxy\\path'; + const testArgs = mockArgs({ + destination: testPath, + proxyAppDestination: proxyPath, + }); + const templatesWithProxy = [...testTemplates, 'node-app-proxy']; + expect(await getDestinations(testArgs, templatesWithProxy)).to.deep.equal({ + destination: testPath, + proxyAppDestination: proxyPath, + }); + }); - const mockDestination = 'my\\path'; + it('should prompt to get base destination when args.destination is empty', async () => { + const testPath = 'test\\path'; inquirerPromptStub.returns({ - destination: mockDestination, + destination: testPath, + }); + const testArgs = mockArgs({ + destination: undefined, }); + await getDestinations(testArgs, testTemplates); + expect(inquirerPromptStub).to.have.been.calledOnce; + expect(inquirerPromptStub.getCall(0).args[0].message).to.be.equal( + 'Where would you like your new app created?' + ); + }); - const args = mockArgs({ - templates: 'foo,bar', + it('should prompt for both base and proxy when destinations are missing in args and templates contain proxy app', async () => { + const testPath = 'test\\path'; + const proxyPath = 'proxy\\path'; + inquirerPromptStub.onCall(0).returns({ + destination: testPath, }); - const expectedTemplates = ['foo', 'bar']; - const expectedDestinationDefault = `${process.cwd()}${sep + expectedTemplates[0]}`; + // avoid paths being equal - this case is tested further down + inquirerPromptStub.onCall(1).returns({ + destination: proxyPath, + }); + const testArgs = mockArgs({ + destination: undefined, + proxyAppDestination: undefined, + }); + const templatesWithProxy = [...testTemplates, 'node-app-proxy']; + await getDestinations(testArgs, templatesWithProxy); + expect(inquirerPromptStub).to.have.been.calledTwice; + expect(inquirerPromptStub.getCall(0).args[0].message).to.be.equal( + 'Where would you like your new app created?' + ); + expect(inquirerPromptStub.getCall(1).args[0].message).to.be.equal( + 'Where would you like your proxy app created?' + ); + }); - await main(args); + it('should return default base destination with base template when --yes arg is used', async () => { + const testArgs = mockArgs({ + destination: undefined, + yes: true, + }); + const expectedDestination = `${process.cwd()}${sep + testTemplates[0]}`; + expect(await getDestinations(testArgs, testTemplates)).to.deep.equal({ + destination: expectedDestination, + }); + }); - expect(inquirerPromptStub).to.have.been.called; - expect(inquirerPromptStub.getCall(0).args[0].default()).to.equal( - expectedDestinationDefault + it('should return default base destination with args.appName when provided and --yes arg is used', async () => { + const testAppName = 'myapp'; + const testArgs = mockArgs({ + destination: undefined, + appName: testAppName, + yes: true, + }); + const expectedDestination = `${process.cwd()}${sep + testAppName}`; + expect(await getDestinations(testArgs, testTemplates)).to.deep.equal({ + destination: expectedDestination, + }); + }); + + it('should return default proxy destination when -- yes arg is used', async () => { + const testPath = 'test\\path'; + const testArgs = mockArgs({ + destination: testPath, + yes: true, + }); + const templatesWithProxy = [...testTemplates, 'node-app-proxy']; + const expectedProxyDestination = `${process.cwd()}${sep + 'node-app-proxy'}`; + expect(await getDestinations(testArgs, templatesWithProxy)).to.deep.equal({ + destination: testPath, + proxyAppDestination: expectedProxyDestination, + }); + }); + + it('should prompt for proxy destination again if proxy destination is the same as base destination', async () => { + const testPath = 'test\\path'; + const proxyPath = 'proxy\\path'; + // avoid paths being equal - this case is tested further down + inquirerPromptStub.onCall(0).returns({ + destination: proxyPath, + }); + const testArgs = mockArgs({ + destination: testPath, + proxyAppDestination: testPath, + }); + const templatesWithProxy = [...testTemplates, 'node-app-proxy']; + await getDestinations(testArgs, templatesWithProxy); + expect(inquirerPromptStub).to.have.been.calledOnce; + expect(inquirerPromptStub.getCall(0).args[0].message).to.be.equal( + 'Proxy app and base app cannot be located in the same folder. Please input another path for proxy' ); - expect(initRunnerStub).to.have.been.calledWith(expectedTemplates, { - ...args, - destination: mockDestination, - templates: expectedTemplates, + }); + + it('should throw when templates are empty', async () => { + const testArgs = mockArgs(); + await getDestinations(testArgs, []).catch((error) => { + expect(error.message).to.be.equal( + 'Unable to get destinations, provided templates are empty' + ); }); }); + }); - it('should respect yes', async () => { + // this partially duplicates tests for getDestinations, but we need to ensure initRunnerStub is called with correct values + // no way around it however - sinon cannot mock getDestinations on its own, which could've prevented this + describe('main with destinations from args', () => { + it('should call initRunnerStub with values from getDestinations', async () => { getAllTemplatesStub.returns(['foo', 'bar']); getBaseTemplatesStub.returns(['foo']); fsExistsSyncStub.returns(false); fsReaddirSyncStub.returns([]); + const mockDestination = 'my\\path'; + const proxyDestination = 'my\\proxy'; + const args = mockArgs({ templates: 'foo', - appName: 'testApp', - yes: true, + destination: mockDestination, + proxyAppDestination: proxyDestination, }); const expectedTemplates = ['foo']; - const expectedDestinationDefault = `${process.cwd()}${sep + args.appName}`; await main(args); - expect(inquirerPromptStub).to.not.have.been.called; - expect(initRunnerStub).to.have.been.calledOnceWith(expectedTemplates, { + expect(initRunnerStub).to.have.been.calledWith(expectedTemplates, { ...args, - destination: expectedDestinationDefault, + destination: mockDestination, + proxyAppDestination: proxyDestination, templates: expectedTemplates, }); }); diff --git a/packages/create-sitecore-jss/src/bin.ts b/packages/create-sitecore-jss/src/bin.ts index 88d6d6a86b..c4156985ff 100644 --- a/packages/create-sitecore-jss/src/bin.ts +++ b/packages/create-sitecore-jss/src/bin.ts @@ -6,13 +6,23 @@ import { initRunner } from './init-runner'; import minimist, { ParsedArgs } from 'minimist'; import { getAllTemplates, getBaseTemplates } from './common'; +const proxyAppMatcher = /node-.+-proxy/g; + export const parseArgs = (): ParsedArgs => { // parse any command line arguments passed into `init sitecore-jss` // to pass to the generator prompts and skip them. // useful for CI and testing purposes const options = { boolean: ['appPrefix', 'force', 'noInstall', 'yes', 'silent', 'prePushHook'], - string: ['appName', 'destination', 'templates', 'hostName', 'fetchWith', 'language'], + string: [ + 'appName', + 'destination', + 'proxyAppDestination', + 'templates', + 'hostName', + 'fetchWith', + 'language', + ], default: { prePushHook: null }, }; const args: ParsedArgs = minimist(process.argv.slice(2), options); @@ -26,6 +36,65 @@ export const parseArgs = (): ParsedArgs => { return args; }; +export const getDestinations = async (args: ParsedArgs, templates: string[]) => { + if (templates.length === 0) { + throw new Error('Unable to get destinations, provided templates are empty'); + } + // validate/gather destinations + const defaultBaseDestination = `${process.cwd()}${ + args.appName ? sep + args.appName : `${sep}${templates[0]}` + }`; + let destination = args.destination; + if (!destination) { + destination = args.yes + ? defaultBaseDestination + : await promptDestination( + 'Where would you like your new app created?', + defaultBaseDestination + ); + } + + // work with node-proxy destination if needed + const proxyApp = templates.find((template) => template.match(proxyAppMatcher)); + if (proxyApp) { + const defaultProxyDestination = proxyApp && `${process.cwd()}${sep}${proxyApp}`; + let proxyAppDestination = args.proxyAppDestination; + if (!proxyAppDestination) { + proxyAppDestination = args.yes + ? defaultProxyDestination + : await promptDestination( + 'Where would you like your proxy app created?', + defaultProxyDestination + ); + } + while (proxyAppDestination === destination) { + proxyAppDestination = await promptDestination( + 'Proxy app and base app cannot be located in the same folder. Please input another path for proxy', + defaultProxyDestination + ); + } + return { + destination, + proxyAppDestination, + }; + } + + return { + destination, + }; +}; + +export const promptDestination = async (prompt: string, defaultDestination: string) => { + return ( + await inquirer.prompt({ + type: 'input', + name: 'destination', + message: prompt, + default: () => defaultDestination, + }) + ).destination; +}; + export const main = async (args: ParsedArgs) => { let templates: string[] = []; @@ -64,39 +133,22 @@ export const main = async (args: ParsedArgs) => { templates.push(answer.template); } - // validate/gather destination - const defaultDestination = `${process.cwd()}${ - args.appName ? sep + args.appName : `${sep}${templates[0]}` - }`; - - let destination = args.destination; + const destinations = await getDestinations(args, templates); - if (!destination) { - if (args.yes) { - destination = defaultDestination; - } else { + for (const destination of [destinations.destination, destinations.proxyAppDestination]) { + if (!destination) continue; + if (!args.force && fs.existsSync(destination) && fs.readdirSync(destination).length > 0) { const answer = await inquirer.prompt({ - type: 'input', - name: 'destination', - message: 'Where would you like your new app created?', - default: () => defaultDestination, + type: 'confirm', + name: 'continue', + message: `Directory '${destination}' not empty. Are you sure you want to continue?`, }); - - destination = answer.destination; - } - } - - if (!args.force && fs.existsSync(destination) && fs.readdirSync(destination).length > 0) { - const answer = await inquirer.prompt({ - type: 'confirm', - name: 'continue', - message: `Directory '${destination}' not empty. Are you sure you want to continue?`, - }); - if (!answer.continue) { - process.exit(); + if (!answer.continue) { + process.exit(); + } + } else { + args.force = true; } - } else { - args.force = true; } if (!args.yes) { @@ -117,7 +169,7 @@ export const main = async (args: ParsedArgs) => { } try { - await initRunner(templates.slice(), { ...args, destination, templates }); + await initRunner(templates.slice(), { ...args, ...destinations, templates }); } catch (error) { console.log(chalk.red('An error occurred: ', error)); process.exit(1); diff --git a/packages/create-sitecore-jss/src/common/args/base.ts b/packages/create-sitecore-jss/src/common/args/base.ts index 324b9febad..9733217106 100644 --- a/packages/create-sitecore-jss/src/common/args/base.ts +++ b/packages/create-sitecore-jss/src/common/args/base.ts @@ -33,6 +33,10 @@ export type BaseArgs = { * Pre-push hook for linting check */ prePushHook?: boolean; + /** + * Optional destination for proxy app, whether it's initialized alongside the main one or standalone + */ + proxyAppDestination?: string; }; /** diff --git a/packages/create-sitecore-jss/src/common/processes/install.test.ts b/packages/create-sitecore-jss/src/common/processes/install.test.ts index 7f2c3bb6ae..6bf316b66d 100644 --- a/packages/create-sitecore-jss/src/common/processes/install.test.ts +++ b/packages/create-sitecore-jss/src/common/processes/install.test.ts @@ -151,11 +151,22 @@ describe('install', () => { }); describe('installPrePushHook', () => { - const execStub = sinon.stub(childProcess, 'exec'); + let execStub: SinonStub; + beforeEach(() => { + execStub = sinon.stub(childProcess, 'exec'); + }); + + afterEach(() => { + execStub?.restore(); + }); it('should run exec function', () => { const destination = './some/path'; - + openPackageJson = sinon.stub(helpers, 'openPackageJson').returns({ + scripts: { + 'install-pre-push-hook': 'stub', + }, + }); installPrePushHook(destination); expect(log).to.have.been.calledOnceWith(chalk.cyan('Installing pre-push hook...')); @@ -167,7 +178,11 @@ describe('install', () => { it('should respect silent', () => { const destination = './some/path'; const silent = true; - + openPackageJson = sinon.stub(helpers, 'openPackageJson').returns({ + scripts: { + 'install-pre-push-hook': 'stub', + }, + }); installPrePushHook(destination, silent); expect(log).to.not.have.been.called; @@ -176,11 +191,27 @@ describe('install', () => { ); }); + it('should skip if installPrePushHook script not defined', () => { + const destination = './some/path'; + openPackageJson = sinon.stub(helpers, 'openPackageJson').returns({ + scripts: {}, + }); + + installPrePushHook(destination); + + expect(log).to.not.have.been.called; + expect(execStub).to.not.have.been.called; + }); + it('should log a warning message if there is an error', async () => { const destination = './some/path'; const error = new Error('some error'); execStub.yields(error); - + openPackageJson = sinon.stub(helpers, 'openPackageJson').returns({ + scripts: { + 'install-pre-push-hook': 'stub', + }, + }); try { await installPrePushHook(destination); } catch (err) { diff --git a/packages/create-sitecore-jss/src/common/processes/install.ts b/packages/create-sitecore-jss/src/common/processes/install.ts index 16f22207ea..17fcc27789 100644 --- a/packages/create-sitecore-jss/src/common/processes/install.ts +++ b/packages/create-sitecore-jss/src/common/processes/install.ts @@ -43,8 +43,7 @@ export const installPackages = (projectFolder: string, silent?: boolean) => { * @param {boolean} [silent] suppress logs */ export const lintFix = (projectFolder: string, silent?: boolean) => { - const packagePath = path.join(projectFolder, 'package.json'); - const pkg = openPackageJson(packagePath); + const pkg = getPackageJson(projectFolder); if (!pkg?.scripts?.lint) { return; } @@ -67,8 +66,12 @@ export const lintFix = (projectFolder: string, silent?: boolean) => { * @param {boolean} [silent] suppress logs */ export const installPrePushHook = async (destination: string, silent?: boolean) => { - silent || console.log(chalk.cyan('Installing pre-push hook...')); + const pkg = getPackageJson(destination); + if (!pkg?.scripts || !pkg.scripts['install-pre-push-hook']) { + return; + } + silent || console.log(chalk.cyan('Installing pre-push hook...')); await new Promise((resolve, reject) => { exec(`cd ${destination} && git init && npm run install-pre-push-hook`, (err) => { if (err) { @@ -81,3 +84,8 @@ export const installPrePushHook = async (destination: string, silent?: boolean) }); }); }; + +const getPackageJson = (projectFolder: string) => { + const packagePath = path.join(projectFolder, 'package.json'); + return openPackageJson(packagePath); +}; diff --git a/packages/create-sitecore-jss/src/common/processes/transform.test.ts b/packages/create-sitecore-jss/src/common/processes/transform.test.ts index e9a1cc8d13..6b91bef4f0 100644 --- a/packages/create-sitecore-jss/src/common/processes/transform.test.ts +++ b/packages/create-sitecore-jss/src/common/processes/transform.test.ts @@ -533,6 +533,42 @@ describe('transform', () => { }); }); + it('should transform file in proxy destination when processing proxy and proxy location destination', async () => { + const templatePath = path.resolve('templates/node-app-proxy'); + const destinationPath = path.resolve('samples/next'); + const destinationProxy = path.resolve('samples/proxy'); + const file = 'file.ts'; + const renderFileOutput = 'file output'; + + globSyncStub = sinon.stub(glob, 'sync').returns([file]); + ejsRenderFileStub = sinon.stub(ejs, 'renderFile').returns(Promise.resolve(renderFileOutput)); + diffAndWriteFilesStub = sinon.stub(transform, 'diffAndWriteFiles'); + + const answers = { + destination: destinationPath, + proxyAppDestination: destinationProxy, + templates: [], + appPrefix: false, + force: false, + }; + + await transformFunc(templatePath, answers); + + expect(ejsRenderFileStub).to.have.been.calledOnceWith(path.join(templatePath, file), { + ...answers, + helper: { + isDev: false, + getPascalCaseName: helpers.getPascalCaseName, + getAppPrefix: helpers.getAppPrefix, + }, + }); + expect(diffAndWriteFilesStub).to.have.been.calledOnceWith({ + rendered: renderFileOutput, + pathToNewFile: path.join(destinationProxy, file), + answers, + }); + }); + it('should skip if isFileForSkip', async () => { const templatePath = path.resolve('templates/next'); const destinationPath = path.resolve('samples/next'); diff --git a/packages/create-sitecore-jss/src/common/processes/transform.ts b/packages/create-sitecore-jss/src/common/processes/transform.ts index 2533866c60..f8034dc631 100644 --- a/packages/create-sitecore-jss/src/common/processes/transform.ts +++ b/packages/create-sitecore-jss/src/common/processes/transform.ts @@ -230,8 +230,12 @@ export const transform = async ( options: TransformOptions = {} ) => { const { isFileForCopy, isFileForSkip, fileForCopyRegExp = FILE_FOR_COPY_REGEXP } = options; - - const destinationPath = path.resolve(answers.destination); + let destination = undefined; + // allow proxy app to be installed separately alongside base app + if (templatePath.match(/.*node-.+-proxy$/g)) { + destination = answers.proxyAppDestination; + } + const destinationPath = path.resolve(destination || answers.destination); if (!answers.appPrefix) { answers.appPrefix = false; @@ -241,7 +245,7 @@ export const transform = async ( const ejsData: Data = { ...answers, helper: { - isDev: isDevEnvironment(answers.destination), + isDev: isDevEnvironment(destination || answers.destination), getPascalCaseName: getPascalCaseName, getAppPrefix: getAppPrefix, }, diff --git a/packages/create-sitecore-jss/src/init-runner.test.ts b/packages/create-sitecore-jss/src/init-runner.test.ts index cd8d8bb648..25f24c21de 100644 --- a/packages/create-sitecore-jss/src/init-runner.test.ts +++ b/packages/create-sitecore-jss/src/init-runner.test.ts @@ -74,6 +74,40 @@ describe('initRunner', () => { expect(nextStepsStub).to.be.calledOnceWith(appName, []); }); + it('should run for both base and proxy path when latter is provided', async () => { + const templates = ['foo', 'bar']; + const appName = 'test-app'; + const args = { + silent: false, + destination: 'samples/next', + proxyAppDestination: 'samples/proxy', + templates, + }; + + const mockFoo = mockInitializer(true, { appName }); + const mockBar = mockInitializer(false, { appName }); + createStub = sinon.stub(InitializerFactory.prototype, 'create'); + createStub.withArgs('foo').returns(mockFoo); + createStub.withArgs('bar').returns(mockBar); + + await initRunner(templates, args); + + expect(log.getCalls().length).to.equal(2); + templates.forEach((template, i) => { + expect(log.getCall(i).args[0]).to.equal(chalk.cyan(`Initializing '${template}'...`)); + }); + expect(mockFoo.init).to.be.calledOnceWith(args); + expect(mockBar.init).to.be.calledOnceWith(args); + + expect(installPackagesStub).to.be.calledTwice; + expect(installPackagesStub.getCall(0).args[0]).to.equal(args.destination); + expect(installPackagesStub.getCall(1).args[0]).to.equal(args.proxyAppDestination); + + expect(lintFixStub).to.be.calledTwice; + expect(lintFixStub.getCall(0).args[0]).to.equal(args.destination); + expect(lintFixStub.getCall(1).args[0]).to.equal(args.proxyAppDestination); + }); + it('should process returned initializers', async () => { const templates = ['foo', 'bar', 'zoo']; const appName = 'test-app'; diff --git a/packages/create-sitecore-jss/src/init-runner.ts b/packages/create-sitecore-jss/src/init-runner.ts index 1fa258094e..a174a0b78a 100644 --- a/packages/create-sitecore-jss/src/init-runner.ts +++ b/packages/create-sitecore-jss/src/init-runner.ts @@ -42,15 +42,19 @@ export const initRunner = async (initializers: string[], args: BaseArgs) => { await runner(initializers); saveConfiguration(args.templates, path.resolve(`${args.destination}${sep}package.json`)); - // final steps (install, lint, etc) - if (!args.noInstall) { - installPackages(args.destination, args.silent); - lintFix(args.destination, args.silent); - } - // install pre-push hook if user opts-in - if (args.prePushHook) { - await installPrePushHook(args.destination, args.silent); + for (const destination of [args.destination, args.proxyAppDestination]) { + if (!destination) continue; + // final steps (install, lint, etc) + if (!args.noInstall) { + installPackages(destination, args.silent); + lintFix(destination, args.silent); + } + + // install pre-push hook if user opts-in + if (args.prePushHook) { + await installPrePushHook(destination, args.silent); + } } if (!args.silent) { diff --git a/packages/create-sitecore-jss/src/initializers/angular/index.ts b/packages/create-sitecore-jss/src/initializers/angular/index.ts index d33cdc579a..07f16f3a23 100644 --- a/packages/create-sitecore-jss/src/initializers/angular/index.ts +++ b/packages/create-sitecore-jss/src/initializers/angular/index.ts @@ -29,7 +29,8 @@ export default class AngularInitializer implements Initializer { addInitializers.push('node-xmcloud-proxy'); } } else { - if (!args.templates.includes('angular-sxp')) { + // invoke default non-XMC init + if (!args.templates.includes('angular-sxp') && !args.templates.includes('angular-xmcloud')) { addInitializers.push('angular-sxp'); } } diff --git a/packages/create-sitecore-jss/src/initializers/node-xmcloud-proxy/index.ts b/packages/create-sitecore-jss/src/initializers/node-xmcloud-proxy/index.ts index 2f29a3752b..e6e2c82c73 100644 --- a/packages/create-sitecore-jss/src/initializers/node-xmcloud-proxy/index.ts +++ b/packages/create-sitecore-jss/src/initializers/node-xmcloud-proxy/index.ts @@ -1,11 +1,14 @@ -import { Initializer } from '../../common'; +import path from 'path'; +import { Initializer, transform, BaseArgs } from '../../common'; export default class AngularXmCloudInitializer implements Initializer { get isBase(): boolean { - return false; + return true; } - async init() { + async init(args: BaseArgs) { + const templatePath = path.resolve(__dirname, '../../templates/node-xmcloud-proxy'); + await transform(templatePath, args); const response = { appName: 'node-xmcloud-proxy', }; diff --git a/packages/create-sitecore-jss/src/templates/angular-xmcloud/package.json b/packages/create-sitecore-jss/src/templates/angular-xmcloud/package.json new file mode 100644 index 0000000000..92f4d857e1 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/angular-xmcloud/package.json @@ -0,0 +1,4 @@ +{ + "license": "yo-ho-ho" +} + \ No newline at end of file diff --git a/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/package.json b/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/package.json new file mode 100644 index 0000000000..f9b9ad86a6 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/package.json @@ -0,0 +1,29 @@ +{ + "name": "node-xmcloud-sample", + "version": "22.1.0-canary", + "description": "Node server-side-rendering proxy sample for running JSS apps under Node hosting for XM Cloud", + "scripts": { + "start": "ts-node ./src/index.ts" + }, + "author": { + "name": "Sitecore Corporation", + "url": "https://jss.sitecore.com" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sitecore/jss.git" + }, + "bugs": { + "url": "https://github.com/sitecore/jss/issues" + }, + "homepage": "https://jss.sitecore.com", + "license": "Apache-2.0", + "dependencies": { + "ts-node": "^10.9.1" + }, + "devDependencies": { + "@types/node": "^20.14.2" + }, + "private": true +} + \ No newline at end of file diff --git a/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/src/index.ts b/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/src/index.ts new file mode 100644 index 0000000000..b316592aa9 --- /dev/null +++ b/packages/create-sitecore-jss/src/templates/node-xmcloud-proxy/src/index.ts @@ -0,0 +1 @@ +console.log('Proxy online, systems nominal');