From 33c8abc4da82c2f7f9f6c707dac52494f0c99144 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 17 Dec 2021 12:04:28 +0200 Subject: [PATCH 1/8] Deprecate `jss create` --- packages/sitecore-jss-cli/package.json | 7 - .../src/create/create.source.folder.ts | 59 ---- .../src/create/create.source.github.ts | 190 ------------ .../sitecore-jss-cli/src/create/index.test.ts | 270 ------------------ packages/sitecore-jss-cli/src/create/index.ts | 193 ------------- .../sitecore-jss-cli/src/scripts/create.ts | 216 +------------- 6 files changed, 14 insertions(+), 921 deletions(-) delete mode 100644 packages/sitecore-jss-cli/src/create/create.source.folder.ts delete mode 100644 packages/sitecore-jss-cli/src/create/create.source.github.ts delete mode 100644 packages/sitecore-jss-cli/src/create/index.test.ts delete mode 100644 packages/sitecore-jss-cli/src/create/index.ts diff --git a/packages/sitecore-jss-cli/package.json b/packages/sitecore-jss-cli/package.json index 58a919939b..3663c8a26f 100644 --- a/packages/sitecore-jss-cli/package.json +++ b/packages/sitecore-jss-cli/package.json @@ -34,14 +34,10 @@ }, "dependencies": { "@sitecore-jss/sitecore-jss-dev-tools": "^20.0.0-canary.91", - "axios": "^0.21.1", "chalk": "^2.4.2", "cross-spawn": "^7.0.0", "dotenv": "^8.2.0", "dotenv-expand": "^5.1.0", - "fs-extra": "^8.1.0", - "glob": "^7.1.4", - "jszip": "^3.2.2", "readline-sync": "^1.4.10", "resolve": "^1.12.0", "tmp": "^0.1.0", @@ -50,9 +46,6 @@ "devDependencies": { "@types/chai": "^4.2.3", "@types/cross-spawn": "^6.0.0", - "@types/fs-extra": "^8.0.0", - "@types/glob": "^7.1.1", - "@types/jszip": "^3.1.6", "@types/mocha": "^5.2.7", "@types/node": "^12.7.11", "@types/readline-sync": "^1.4.3", diff --git a/packages/sitecore-jss-cli/src/create/create.source.folder.ts b/packages/sitecore-jss-cli/src/create/create.source.folder.ts deleted file mode 100644 index 1bd7ef53d8..0000000000 --- a/packages/sitecore-jss-cli/src/create/create.source.folder.ts +++ /dev/null @@ -1,59 +0,0 @@ -import chalk from 'chalk'; -import fs from 'fs-extra'; -import path from 'path'; - -export class FolderSource { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - argv: any; - destinationPath: string; - templatePath = ''; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(argv: any, destinationPath: string) { - this.argv = argv; - this.destinationPath = destinationPath; - } - - async getFromSource() { - this.verifyTemplate(); - this.copyTemplate(); - } - - async verifyTemplate() { - if (!fs.existsSync(this.argv.source)) { - console.error(chalk.red(`The source path ${this.argv.source} was not a valid directory.`)); - process.exit(1); - } - - const templatePath = path.join(this.argv.source, this.argv.template); - - if (!fs.existsSync(templatePath)) { - console.error( - chalk.red( - `The template ${this.argv.template} did not exist in source path ${this.argv.source}.` - ) - ); - process.exit(1); - } - - this.templatePath = templatePath; - } - - copyTemplate() { - console.log( - chalk.cyan(`Copying template from ${this.templatePath} to ${this.destinationPath}...`) - ); - - fs.copySync(this.templatePath, this.destinationPath, { - filter: (fullPath) => { - const ignored = /(node_modules|scjssconfig.json|deploysecret)/.test(fullPath); - - if (!ignored) { - console.log(chalk.gray(`copy ${fullPath}...`)); - } - - return !ignored; - }, - }); - } -} diff --git a/packages/sitecore-jss-cli/src/create/create.source.github.ts b/packages/sitecore-jss-cli/src/create/create.source.github.ts deleted file mode 100644 index 61025a4b32..0000000000 --- a/packages/sitecore-jss-cli/src/create/create.source.github.ts +++ /dev/null @@ -1,190 +0,0 @@ -import chalk from 'chalk'; -import fs from 'fs'; -import JSZip from 'jszip'; -import path from 'path'; -import axios, { AxiosError } from 'axios'; -import tmp from 'tmp'; - -export class GitHubSource { - branch: string; - githubListApi: string; - githubDownloadUrl: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - argv: any; - destinationPath: string; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(argv: any, destinationPath: string) { - const repository = argv.repository || 'Sitecore/jss'; - const branch = argv.branch || 'master'; - - this.branch = branch; - this.githubListApi = `https://api.github.com/repos/${repository}/contents/samples?ref=${branch}`; - this.githubDownloadUrl = `https://github.com/${repository}/archive/${branch}.zip`; - this.argv = argv; - this.destinationPath = destinationPath; - } - - async getFromSource() { - await this.verifyTemplate(); - - return new Promise((resolve, reject) => { - tmp.setGracefulCleanup(); - - // unsafeCleanup just means it'll kill any files left in the temp folder - // that we've created. - tmp.dir({ unsafeCleanup: true }, async (err, tempDir, cleanupTempDir) => { - if (err) { - reject(err); - } - - const zipFileName = path.join(tempDir, 'jss.zip'); - await this.downloadRepo(zipFileName); - - await this.extractTemplateFiles(zipFileName); - - cleanupTempDir(); - - resolve(); - }); - }); - } - - async verifyTemplate() { - return new Promise((resolve) => { - axios - .get(this.githubListApi, { - headers: { 'User-Agent': 'SitecoreJSSCLI' }, - proxy: this.extractProxy(), - }) - .then((response) => { - const body = response.data; - - if (!body || !Array.isArray(body)) { - console.log(body); - console.error( - chalk.red( - 'Received unexpected response from server while trying to enumerate templates.' - ) - ); - process.exit(1); - } - - const apiResult: Array<{ name: string }> = body; - if (!apiResult.some((result) => result.name === this.argv.template)) { - console.error(chalk.red(`Template ${this.argv.template} did not exist.`)); - console.error(chalk.red('Valid templates are: ')); - apiResult.forEach((result) => { - if (result.name === 'node-headless-ssr-proxy') { - return; - } - - console.error(chalk.yellow(result.name)); - }); - process.exit(1); - } - - resolve(apiResult); - }) - .catch((error: AxiosError) => { - if (error.response) { - console.error( - chalk.red( - `Server sent ${error.response.status} ${error.response.statusText} while enumerating templates.` - ) - ); - } else { - console.error( - chalk.red(`Unexpected error ${error.message} while trying to enumerate templates.`) - ); - } - - process.exit(1); - }); - }); - } - - async downloadRepo(fileName: string) { - console.log(chalk.cyan(`Acquiring templates from ${this.githubDownloadUrl}...`)); - - await new Promise((resolve, reject) => { - axios - .get(this.githubDownloadUrl, { - proxy: this.extractProxy(), - headers: { 'User-Agent': 'SitecoreJSSCLI' }, - responseType: 'stream', - }) - .then((response) => { - const fileStream = fs.createWriteStream(fileName, { autoClose: true }); - response.data.pipe(fileStream); - response.data.on('error', reject); - fileStream.on('finish', resolve); - }); - }); - } - - async extractTemplateFiles(zipFile: string) { - console.log(chalk.cyan(`Extracting template ${this.argv.template}...`)); - - const filter = new RegExp(`^[^/]+/samples/${this.argv.template}/(.+)`); - - fs.mkdirSync(this.destinationPath); - - return new Promise((resolve, reject) => { - fs.readFile(zipFile, (err, data) => { - if (err) { - reject(err); - } - - const jszip = new JSZip(); - const writePromises: Array> = []; - - jszip.loadAsync(data).then(async (zip) => { - zip - .filter((innerPath) => filter.test(innerPath)) - .forEach((file) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const relativePath = (filter.exec(file.name) as any)[1]; - const outputPath = path.join(this.destinationPath, relativePath); - - if (file.dir) { - // create directory - if (!fs.existsSync(outputPath)) { - console.log(chalk.gray(`mkdir ${outputPath}`)); - fs.mkdirSync(outputPath); - } - } else { - // read file from zip, write to file - writePromises.push( - file.async('nodebuffer').then((content) => { - console.log(chalk.gray(`write ${outputPath}`)); - fs.writeFileSync(outputPath, content); - }) - ); - } - }); - - await Promise.all(writePromises); - resolve(); - }); - }); - }); - } - - private extractProxy() { - if (!this.argv.proxy) return undefined; - - try { - const proxy = new URL(this.argv.proxy); - - return { - protocol: proxy.protocol.slice(0, -1), - host: proxy.hostname, - port: +proxy.port, - }; - } catch (error) { - console.error(chalk.red(`Invalid proxy url provided ${this.argv.proxy}`)); - process.exit(1); - } - } -} diff --git a/packages/sitecore-jss-cli/src/create/index.test.ts b/packages/sitecore-jss-cli/src/create/index.test.ts deleted file mode 100644 index 72e88e134c..0000000000 --- a/packages/sitecore-jss-cli/src/create/index.test.ts +++ /dev/null @@ -1,270 +0,0 @@ -/* eslint-disable no-unused-expressions */ -import { expect } from 'chai'; -import { - applyNameToPackageJson, - applyHostNameToSitecoreConfig, - applyNameReplacement, - getPascalCaseName, -} from './index'; - -describe('applyNameReplacement', () => { - const mockConfig = (appName: string, customPath?: string) => { - return ` - - - - - - - - - - - - - - <${appName}GraphQLEndpoint url="${customPath ? customPath : '/api/'}${appName}"> - - - - - ${ - customPath ? customPath : '/sitecore/templates/Project/' - }${appName} - - - - - - - - - - - `; - }; - - it('should replace name', () => { - const result = applyNameReplacement('this is a test.', 'test', 'passing test'); - expect(result).to.equal('this is a passing test.'); - }); - - it('should replace multiple names', () => { - const result = applyNameReplacement('this is a test. yay for testing.', 'test', 'passing test'); - expect(result).to.equal('this is a passing test. yay for passing testing.'); - }); - - it('should replace multiline', () => { - const result = applyNameReplacement( - ` - - this is a test - - `, - 'test', - 'passingTest' - ); - expect(result).to.equal( - ` - - this is a passingTest - - ` - ); - }); - - it('should handle special RegEx characters in replaceName', () => { - const result = applyNameReplacement('this is a te$+.', 'te$+', 'passing test'); - expect(result).to.equal('this is a passing test.'); - }); - - it('should apply name using replaceName', () => { - const config = mockConfig('FooApp', '/somewhere/else/'); - const result = applyNameReplacement(config, 'FooApp', 'Bar'); - expect(result).to.match(/ { - const config = mockConfig('FooApp', '/somewhere/else/'); - const result = applyNameReplacement(config, 'BarApp', 'Bar'); - expect(result).to.not.match(/ { - it('should apply name using replaceName', () => { - const result = applyNameToPackageJson( - { - name: 'FooName', - config: { - appName: 'FooAppName', - sitecoreDistPath: '/somewhere/dist/FooAppName', - }, - }, - 'bar', - 'FooAppName' - ); - expect(result.name).to.equal('bar'); - expect(result.config.appName).to.equal('bar'); - expect(result.config.sitecoreDistPath).to.equal('/somewhere/dist/bar'); - }); - - it('should apply prefix for rootPlaceholders', () => { - const result = applyNameToPackageJson( - { - name: 'FooName', - config: { - appName: 'FooAppName', - sitecoreDistPath: '/somewhere/dist/FooAppName', - rootPlaceholders: ['FooAppName-jss-main', 'FooAppName-jss-header'], - }, - }, - 'bar', - 'FooAppName', - true - ); - expect(result.name).to.equal('bar'); - expect(result.config.appName).to.equal('bar'); - expect(result.config.sitecoreDistPath).to.equal('/somewhere/dist/bar'); - expect(result.config.rootPlaceholders).to.deep.equal(['bar-jss-main', 'bar-jss-header']); - }); - - it('should remove prefix from the rootPlaceholders', () => { - const result = applyNameToPackageJson( - { - name: 'FooName', - config: { - appName: 'FooAppName', - sitecoreDistPath: '/somewhere/dist/FooAppName', - rootPlaceholders: ['FooAppName-jss-main', 'FooAppName-jss-header'], - }, - }, - 'bar', - 'FooAppName', - false - ); - expect(result.name).to.equal('bar'); - expect(result.config.appName).to.equal('bar'); - expect(result.config.sitecoreDistPath).to.equal('/somewhere/dist/bar'); - expect(result.config.rootPlaceholders).to.deep.equal(['jss-main', 'jss-header']); - }); - - it('should not apply name using replaceName if no match', () => { - const result = applyNameToPackageJson( - { - name: 'FooName', - config: { - appName: 'FooAppName', - sitecoreDistPath: '/somewhere/dist/FooAppName', - graphQLEndpointPath: '/somewhere/api/FooAppName', - }, - }, - 'bar', - 'BarAppName' - ); - expect(result.name).to.equal('bar'); - expect(result.config.appName).to.equal('FooAppName'); - expect(result.config.sitecoreDistPath).to.equal('/somewhere/dist/FooAppName'); - expect(result.config.graphQLEndpointPath).to.equal('/somewhere/api/FooAppName'); - }); - - it('should only apply sitecoreDistPath and graphQLEndpointPath if present', () => { - const result = applyNameToPackageJson( - { - name: 'FooName', - config: { appName: 'FooAppName' }, - }, - 'bar', - 'JssTestWeb' - ); - expect(result.config.sitecoreDistPath).to.be.undefined; - expect(result.config.graphQLEndpointPath).to.be.undefined; - }); -}); - -describe('applyHostNameToSitecoreConfig', () => { - const mockConfig = () => { - return ` - - - - - - - - `; - }; - - it('should apply hostName', () => { - const config = mockConfig(); - const result = applyHostNameToSitecoreConfig(config, 'bar.localhost'); - expect(result).to.match(/ { - it('should reformat kebab-case to PascalCase', () => { - const result = getPascalCaseName('my-next-sitecore-app'); - - expect(result).to.match(/MyNextSitecoreApp/); - }); - - it('should reformat snake_case to PascalCase', () => { - const result = getPascalCaseName('my_next_sitecore_app'); - - expect(result).to.match(/MyNextSitecoreApp/); - }); - - it('should reformat one word lowercase app name to be capitalized', () => { - const result = getPascalCaseName('onewordappnamenohyphen'); - - expect(result).to.match(/Onewordappnamenohyphen/); - }); -}); diff --git a/packages/sitecore-jss-cli/src/create/index.ts b/packages/sitecore-jss-cli/src/create/index.ts deleted file mode 100644 index 169bfa6452..0000000000 --- a/packages/sitecore-jss-cli/src/create/index.ts +++ /dev/null @@ -1,193 +0,0 @@ -import chalk from 'chalk'; -import fs from 'fs'; -import glob from 'glob'; -import path from 'path'; -import runPackageScript from '../run-package-script'; - -interface PackageJsonConfig { - appName: string; - graphQLEndpointPath?: string; - sitecoreDistPath?: string; - rootPlaceholders?: string[]; -} - -interface PackageJson { - name: string; - config: PackageJsonConfig; -} - -/** - * Apply name replacement on a given string - * @param {string} value String to perform replacement on - * @param {string} replaceName Name value to replace - * @param {string} withName Name value to replace with - */ -export function applyNameReplacement(value: string, replaceName: string, withName: string) { - // Escapes a string literal for use in RegEx - const escapeLiteral = (literal: string) => { - return literal.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - }; - return value.replace(RegExp(escapeLiteral(replaceName), 'g'), withName); -} - -/** - * @param {PackageJson} pkg package.json object - * @param {string} name App name - * @param {string} replaceName Name value to replace - * @param {boolean} withPrefix should replace placeholder prefix - */ -export function applyNameToPackageJson( - pkg: PackageJson, - name: string, - replaceName: string, - withPrefix?: boolean -) { - pkg.name = name; // root name will never match "replaceName", so always set directly - pkg.config.appName = applyNameReplacement(pkg.config.appName, replaceName, name); - - pkg.config.rootPlaceholders = pkg.config.rootPlaceholders?.map((ph) => - applyNameReplacement(ph, withPrefix ? replaceName : `${replaceName}-`, withPrefix ? name : '') - ); - - if (pkg.config.sitecoreDistPath) { - pkg.config.sitecoreDistPath = applyNameReplacement( - pkg.config.sitecoreDistPath, - replaceName, - name - ); - } - return pkg; -} - -/** - * @param {string} configXml Sitecore configuration xml - * @param {string} hostName App hostname - */ -export function applyHostNameToSitecoreConfig(configXml: string, hostName: string) { - // replace host name - configXml = configXml.replace( - / { - let configXml = fs.readFileSync(sitecoreConfigPath, 'utf8'); - - console.log( - chalk.cyan(`Applying name ${name} and hostName ${hostName} to ${sitecoreConfigPath}...`) - ); - configXml = applyNameReplacement(configXml, replaceName, name); - configXml = applyHostNameToSitecoreConfig(configXml, hostName); - - fs.unlinkSync(sitecoreConfigPath); - - const configFileIndexName = configFileIndex === 0 ? '' : configFileIndex; - const finalConfigFileName = path.join( - path.dirname(sitecoreConfigPath), - `${name}${configFileIndexName}.config` - ); - - configFileIndex += 1; - - fs.writeFileSync(finalConfigFileName, configXml); - }); - - replacePrefix(projectFolder, name, replaceName, withPrefix); - - // fix any lint errors created by replacePrefix - console.log(chalk.cyan('Fixing potential linting errors...')); - runPackageScript(['lint', '--fix'], { cwd: projectFolder, encoding: 'utf-8' }); -} - -/** - * Returns a string formatted to PascalCase - * my-next-sitecore-app becomes MyNextSitecoreApp - * @param {string} name - */ -export function getPascalCaseName(name: string): string { - // handle underscores by converting them to hyphens - const temp: string[] = name.replace(/_/g, '-').split('-'); - name = temp.map((item: string) => (item = item.charAt(0).toUpperCase() + item.slice(1))).join(''); - return name; -} - -/** - * Called during jss create, this function replaces the sample's prefix with the app's name on Sitecore templates/placeholders - * @param {string} projectFolder Project folder - * @param {string} name Name value to replace - * @param {string} prefix Prefix of the sample app's template/placeholder - should match Jss[RAV|Next]Web - * @param {boolean} withPrefix if true, replaces prefix with app name in PascalCase, - * otherwise strip the prefix - */ -export function replacePrefix( - projectFolder: string, - name: string, - prefix: string, - withPrefix?: boolean -) { - if (!withPrefix) { - console.log(chalk.cyan('Removing template/placeholder prefix...')); - const prefixWithHyphen = prefix + '-'; - glob - .sync(path.join(projectFolder, './{data,sitecore/definitions,src}/**/*.*'), { - ignore: '{**/*.png,**/*.pdf}', - }) - .forEach((filePath: string) => { - let fileContents: string = fs.readFileSync(filePath, 'utf8'); - - // remove prefix - fileContents = applyNameReplacement(fileContents, prefixWithHyphen, ''); - // need to call applyNameReplacement again with original prefix - // to account for GraphQL queries and associated components - fileContents = applyNameReplacement(fileContents, prefix, ''); - fs.writeFileSync(filePath, fileContents); - }); - return; - } - - console.log( - chalk.cyan(`Replacing template/placeholder prefix with ${getPascalCaseName(name)}...`) - ); - glob - .sync(path.join(projectFolder, './{data,sitecore/definitions,src}/**/*.*'), { - ignore: '{**/*.png,**/*.pdf}', - }) - .forEach((filePath: string) => { - let fileContents: string = fs.readFileSync(filePath, 'utf8'); - - // placeholders - fileContents = applyNameReplacement(fileContents, prefix + '-jss', name + '-jss'); - // replace prefix with pascal case app name - fileContents = applyNameReplacement(fileContents, prefix, getPascalCaseName(name)); - - fs.writeFileSync(filePath, fileContents); - }); -} diff --git a/packages/sitecore-jss-cli/src/scripts/create.ts b/packages/sitecore-jss-cli/src/scripts/create.ts index b38adc338d..6b0cb00e1d 100644 --- a/packages/sitecore-jss-cli/src/scripts/create.ts +++ b/packages/sitecore-jss-cli/src/scripts/create.ts @@ -1,12 +1,5 @@ /* eslint-disable prettier/prettier */ -import chalk from 'chalk'; -import fs from 'fs'; -import path from 'path'; import { Argv } from 'yargs'; -import { FolderSource } from '../create/create.source.folder'; -import { GitHubSource } from '../create/create.source.github'; -import { runPackageManagerCommand } from '../run-package-script'; -import spawn from '../spawn'; /** * @param {any} yargs @@ -14,208 +7,27 @@ import spawn from '../spawn'; export function builder(yargs: Argv) { return yargs.command( 'create