From 244a5a5bb2cfc3e0606ca38b483c1c691a53b4c7 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 21 Jul 2021 10:18:10 +0300 Subject: [PATCH 01/20] [MVC] [NextJS] Add "--empty" flag --- docs/data/routes/docs/fundamentals/cli/en.md | 1 + samples/nextjs/data/routes/en.yml | 1 + samples/nextjs/jss-create.js | 76 +++++++++++++-- samples/nextjs/next.config.base.stripped.js | 89 +++++++++++++++++ samples/nextjs/scripts/bootstrap.stripped.ts | 20 ++++ .../scripts/scaffold-component.stripped.ts | 97 +++++++++++++++++++ .../src/lib/sitemap-factory.stripped.ts | 42 ++++++++ samples/nextjs/src/lib/sitemap-factory.ts | 70 +++++++++++++ samples/nextjs/src/lib/sitemap-fetcher.ts | 67 ------------- samples/nextjs/src/pages/[[...path]].tsx | 6 +- 10 files changed, 391 insertions(+), 78 deletions(-) create mode 100644 samples/nextjs/next.config.base.stripped.js create mode 100644 samples/nextjs/scripts/bootstrap.stripped.ts create mode 100644 samples/nextjs/scripts/scaffold-component.stripped.ts create mode 100644 samples/nextjs/src/lib/sitemap-factory.stripped.ts create mode 100644 samples/nextjs/src/lib/sitemap-factory.ts delete mode 100644 samples/nextjs/src/lib/sitemap-fetcher.ts diff --git a/docs/data/routes/docs/fundamentals/cli/en.md b/docs/data/routes/docs/fundamentals/cli/en.md index 5a05bb56fe..c511c170fc 100644 --- a/docs/data/routes/docs/fundamentals/cli/en.md +++ b/docs/data/routes/docs/fundamentals/cli/en.md @@ -39,6 +39,7 @@ jss create |`--proxy`, `-p` | Specifies a HTTP proxy when downloading templates. | A local directory path | - | all | |`--fetchWith` | Specifies how the applicaiton should fetch Sitecore layout and dictionary data. |`REST` or `GraphQL` | `REST` | nextjs | |`--prerender` | Specifies the Next.js pre-rendering form for the primary `[[...path]].tsx` route. | `SSG` or `SSR` | `SSG` | nextjs | +|`--empty` | Specifies the Next.js app should be created empty | - | `false` | nextjs | **Examples** ``` diff --git a/samples/nextjs/data/routes/en.yml b/samples/nextjs/data/routes/en.yml index 404a828de2..e2b8451242 100644 --- a/samples/nextjs/data/routes/en.yml +++ b/samples/nextjs/data/routes/en.yml @@ -52,6 +52,7 @@ placeholders:

To remove all of the default sample content (the Styleguide and GraphQL routes) and start out with an empty JSS app:

  1. Delete /src/components/Styleguide* and /src/components/GraphQL*
  2. +
  3. Delete graphql-let command from bootstrap npm command in package.json until you create .graphql files
  4. Delete /sitecore/definitions/components/Styleguide*, /sitecore/definitions/templates/Styleguide*, and /sitecore/definitions/components/GraphQL*
  5. Delete /data/component-content/Styleguide
  6. Delete /data/content/Styleguide
  7. diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index c61d15b3a8..859d08964f 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -35,6 +35,12 @@ module.exports = function createJssProject(argv, nextSteps) { ); } + if (argv.empty) { + createEmptyStarter(); + } else { + cleanUpStrippedFiles(); + } + setFetchWith(argv.fetchWith); setPrerender(argv.prerender); setNextConfig(); @@ -43,15 +49,67 @@ module.exports = function createJssProject(argv, nextSteps) { return nextSteps; }; +function getPath(filepath) { + return path.join(__dirname, filepath); +} + +function cleanUpStrippedFiles() { + const files = getStrippedFiles(); + + files.forEach(filepath => fs.unlinkSync(filepath)) +} + +function getStrippedFiles() { + return [ + getPath('scripts/bootstrap.stripped.ts'), + getPath('scripts/scaffold-component.stripped.ts'), + getPath('src/lib/sitemap-factory.stripped.ts'), + getPath('next.config.base.stripped.js'), + ]; +} + +function createEmptyStarter() { + const dataDir = getPath('data'); + const disconnectedProxyScript = getPath('scripts/disconnected-mode-proxy.ts'); + const manifestTemplate = getPath('scripts/templates/component-manifest.ts'); + const definitionsDir = getPath('sitecore/definitions'); + const componentsDir = getPath('src/components'); + + console.log(chalk.cyan('Cleaning up the sample...')); + + const strippedFiles = getStrippedFiles(); + + strippedFiles.forEach(strippedFilePath => { + const defaultFilePath = strippedFilePath.replace('.stripped', ''); + fs.unlinkSync(defaultFilePath) + fs.renameSync(strippedFilePath, defaultFilePath); + }) + + fs.rmdirSync(dataDir, { recursive: true }); + fs.unlinkSync(disconnectedProxyScript); + fs.unlinkSync(manifestTemplate); + fs.rmdirSync(definitionsDir, { recursive: true }); + fs.rmdirSync(componentsDir, { recursive: true }); + + const packageJson = require('./package.json'); + + delete packageJson.scripts.start; + delete packageJson.scripts['start:disconnected-proxy']; + // Temporary solution until we remove graphql-let, graphql-let can't be used if there are no .graphql files + packageJson.scripts.bootstrap = packageJson.scripts.bootstrap.replace(' && graphql-let', ''); + + fs.writeFileSync(getPath('package.json'), JSON.stringify(packageJson), 'utf8'); +} + /** * Sets how Sitecore data (layout, dictionary) is fetched. * @param {string} [fetchWith] {REST|GraphQL} Default is REST. */ function setFetchWith(fetchWith) { - const defaultDsfFile = path.join(__dirname, 'src/lib/dictionary-service-factory.ts'); - const restDsfFile = path.join(__dirname, 'src/lib/dictionary-service-factory.rest.ts'); - const defaultLsfFile = path.join(__dirname, 'src/lib/layout-service-factory.ts'); - const restLsfFile = path.join(__dirname, 'src/lib/layout-service-factory.rest.ts'); + const defaultDsfFile = getPath('src/lib/dictionary-service-factory.ts'); + const restDsfFile = getPath('src/lib/dictionary-service-factory.rest.ts'); + const defaultLsfFile = getPath('src/lib/layout-service-factory.ts'); + const restLsfFile = getPath('src/lib/layout-service-factory.rest.ts'); const FetchWith = { GRAPHQL: 'graphql', REST: 'rest', @@ -85,9 +143,9 @@ function setFetchWith(fetchWith) { * @param {string} [prerender] {SSG|SSR} Default is SSG. */ function setPrerender(prerender) { - const defaultRouteFile = path.join(__dirname, 'src/pages/[[...path]].tsx'); - const ssrRouteFile = path.join(__dirname, 'src/pages/[[...path]].SSR.tsx'); - const sitemapFile = path.join(__dirname, 'src/lib/sitemap-fetcher.ts'); + const defaultRouteFile = getPath('src/pages/[[...path]].tsx'); + const ssrRouteFile = getPath('src/pages/[[...path]].SSR.tsx'); + const sitemapFile = getPath('src/lib/sitemap-fetcher.ts'); const Prerender = { SSG: 'ssg', SSR: 'ssr', @@ -118,8 +176,8 @@ function setPrerender(prerender) { * Switch development next.config.js to production config */ function setNextConfig() { - const nextConfig = path.join(__dirname, 'next.config.js'); - const baseConfig = path.join(__dirname, 'next.config.base.js'); + const nextConfig = getPath('next.config.js'); + const baseConfig = getPath('next.config.base.js'); console.log(chalk.cyan('Replacing next.config...')); diff --git a/samples/nextjs/next.config.base.stripped.js b/samples/nextjs/next.config.base.stripped.js new file mode 100644 index 0000000000..c30f57315d --- /dev/null +++ b/samples/nextjs/next.config.base.stripped.js @@ -0,0 +1,89 @@ +const jssConfig = require('./src/temp/config'); +const packageConfig = require('./package.json').config; +const { getPublicUrl } = require('@sitecore-jss/sitecore-jss-nextjs'); + +const publicUrl = getPublicUrl(); + +const nextConfig = { + // Set assetPrefix to our public URL + assetPrefix: publicUrl, + + // Allow specifying a distinct distDir when concurrently running app in a container + distDir: process.env.NEXTJS_DIST_DIR || '.next', + + // Make the same PUBLIC_URL available as an environment variable on the client bundle + env: { + PUBLIC_URL: publicUrl, + }, + + i18n: { + // These are all the locales you want to support in your application. + // These should generally match (or at least be a subset of) those in Sitecore. + locales: ['en', 'da-DK'], + // This is the locale that will be used when visiting a non-locale + // prefixed path e.g. `/styleguide`. + defaultLocale: packageConfig.language, + }, + + async rewrites() { + // When in connected mode we want to proxy Sitecore paths off to Sitecore + return [ + { + source: '/sitecore/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, + }, + { + source: '/:locale/sitecore/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, + }, + // media items + { + source: '/-/:path*', + destination: `${jssConfig.sitecoreApiHost}/-/:path*`, + }, + { + source: '/:locale/-/:path*', + destination: `${jssConfig.sitecoreApiHost}/-/:path*`, + }, + // visitor identification + { + source: '/layouts/:path*', + destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, + }, + { + source: '/:locale/layouts/:path*', + destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, + }, + ]; + }, + + webpack: (config, options) => { + applyGraphQLCodeGenerationLoaders(config, options); + + return config; + }, +}; + +const applyGraphQLCodeGenerationLoaders = (config, options) => { + config.module.rules.push({ + test: /\.graphql$/, + exclude: /node_modules/, + use: [options.defaultLoaders.babel, { loader: 'graphql-let/loader' }], + }); + + config.module.rules.push({ + test: /\.graphqls$/, + exclude: /node_modules/, + use: ['graphql-let/schema/loader'], + }); + + config.module.rules.push({ + test: /\.ya?ml$/, + type: 'json', + use: 'yaml-loader', + }); + + return config; +}; + +module.exports = nextConfig; diff --git a/samples/nextjs/scripts/bootstrap.stripped.ts b/samples/nextjs/scripts/bootstrap.stripped.ts new file mode 100644 index 0000000000..596b68f218 --- /dev/null +++ b/samples/nextjs/scripts/bootstrap.stripped.ts @@ -0,0 +1,20 @@ +import { generateConfig } from './generate-config'; + +/* + BOOTSTRAPPING + The bootstrap process runs before build, and generates JS that needs to be + included into the build - specifically, the component name to component mapping, + and the global config module. +*/ + +/* + CONFIG GENERATION + Generates the /src/temp/config.js file which contains runtime configuration + that the app can import and use. +*/ +generateConfig(); + +/* + COMPONENT FACTORY GENERATION +*/ +import './generate-component-factory'; diff --git a/samples/nextjs/scripts/scaffold-component.stripped.ts b/samples/nextjs/scripts/scaffold-component.stripped.ts new file mode 100644 index 0000000000..012317efe9 --- /dev/null +++ b/samples/nextjs/scripts/scaffold-component.stripped.ts @@ -0,0 +1,97 @@ +/* + Component Scaffolding Script + This is a script that enables scaffolding a new JSS component using `jss scaffold `. + The default convention is that component names must start with a capital letter, and can contain + letters, number, underscores, or dashes. + + If the parameter includes a path, it must be relative to the src/components folder. + For example, `jss scaffold search/SearchBox` will create a component called `SearchBox` in + `src/components/search/SearchBox.tsx`. Specifying a relative path is optional, and just providing + the name is ok. + + Edit this script if you wish to use your own conventions for component storage in your JSS app. +*/ + +/* eslint-disable no-throw-literal,no-console */ + +import fs from 'fs'; +import path from 'path'; +import chalk from 'chalk'; +import generateComponentSrc from './templates/component-src'; + +const componentRootPath = 'src/components'; + +// Matches component names that start with a capital letter, and contain only letters, number, +// underscores, or dashes. Optionally, the component name can be preceded by a relative path +const nameParamFormat = new RegExp(/^((?:[\w-]+\/)*)([A-Z][\w-]+)$/); +const componentArg = process.argv[2]; + +if (!componentArg) { + throw 'Component name was not passed. Usage: jss scaffold '; +} + +const regExResult = nameParamFormat.exec(componentArg); + +if (regExResult === null) { + throw `Component name should start with an uppercase letter and contain only letters, numbers, +dashes, or underscores. If specifying a path, it must be relative to src/components`; +} + +const componentPath = regExResult[1]; +const componentName = regExResult[2]; +const filename = `${componentName}.tsx`; + +const componentOutputPath = scaffoldFile( + componentRootPath, + generateComponentSrc(componentName), + filename +); + +console.log( + chalk.green(` +Scaffolding of ${componentName} complete. +Next steps:`) +); + +console.log( + `* Scaffold the component in Sitecore using '${chalk.green( + `jss deploy component ${componentName} --allowedPlaceholders placeholder-for-component` + )}, or create the rendering item and datasource template yourself.` +); + +if (componentOutputPath) { + console.log(`* Implement the React component in ${chalk.green(componentOutputPath)}`); +} + +console.log(`* Add the component to a route using Sitecore Experience Editor, and test it.`); + +/** + * Force to use `crlf` line endings, we are using `crlf` across the project. + * Replace: `lf` (\n), `cr` (\r) + * @param {string} content + */ +function editLineEndings(content: string) { + return content.replace(/\r|\n/gm, '\r\n'); +} + +/** + * Creates a file relative to the specified path if the file doesn't exist. Creates directories as needed. + * @param {string} rootPath - the root path + * @param {string} fileContent - the file content + * @param {string} filename - the filename + * @returns the new file's filepath + */ +function scaffoldFile(rootPath: string, fileContent: string, filename: string): string | null { + const outputDir = path.join(rootPath, componentPath); + const outputFile = path.join(outputDir, filename); + + if (fs.existsSync(outputFile)) { + console.log(chalk.red(`Skipping creating ${outputFile}; already exists.`)); + return null; + } + + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(outputFile, editLineEndings(fileContent), 'utf8'); + console.log(chalk.green(`File ${outputFile} has been scaffolded.`)); + return outputFile; +} diff --git a/samples/nextjs/src/lib/sitemap-factory.stripped.ts b/samples/nextjs/src/lib/sitemap-factory.stripped.ts new file mode 100644 index 0000000000..a23162285e --- /dev/null +++ b/samples/nextjs/src/lib/sitemap-factory.stripped.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { GraphQLSitemapService, StaticPath } from '@sitecore-jss/sitecore-jss-nextjs'; +import { GetStaticPathsContext } from 'next'; +import config from 'temp/config'; +import { config as packageConfig } from '../../package.json'; + +type SitemapFetchFunction = ( + context?: GetStaticPathsContext +) => Promise | StaticPath[]; + +export class SitemapFactory { + create(): SitemapFetchFunction { + const graphqlSitemapService = new GraphQLSitemapService({ + endpoint: config.graphQLEndpoint, + apiKey: config.sitecoreApiKey, + siteName: config.jssAppName, + /* + The Sitemap Service needs a root item ID in order to fetch the list of pages for the current + app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; + otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. + rootItemId: '{GUID}' + */ + }); + + /** + * Generates SitecoreSitemap for given mode (Export / SSG) + * @param {GetStaticPathsContext} context + */ + const fetch: SitemapFetchFunction = (context) => { + // If we are in Export xport mode + if (process.env.EXPORT_MODE) { + return graphqlSitemapService.fetchExportSitemap(packageConfig.language); + } + + return graphqlSitemapService.fetchSSGSitemap(context?.locales || []); + }; + + return fetch; + } +} + +export const sitemapFactory = new SitemapFactory(); diff --git a/samples/nextjs/src/lib/sitemap-factory.ts b/samples/nextjs/src/lib/sitemap-factory.ts new file mode 100644 index 0000000000..9c1abdfb45 --- /dev/null +++ b/samples/nextjs/src/lib/sitemap-factory.ts @@ -0,0 +1,70 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { + GraphQLSitemapService, + StaticPath, + DisconnectedSitemapService, + ManifestInstance, +} from '@sitecore-jss/sitecore-jss-nextjs'; +import { GetStaticPathsContext } from 'next'; +import config from 'temp/config'; +import { config as packageConfig } from '../../package.json'; + +type SitemapFetchFunction = ( + context?: GetStaticPathsContext +) => Promise | StaticPath[]; + +export class SitemapFactory { + create(): SitemapFetchFunction { + /** + * Get sitecore-import.json manifest + */ + const getManifest = () => { + if (process.env.JSS_MODE !== 'disconnected') return null; + + try { + const manifest = require('sitecore/manifest/sitecore-import.json'); + + return manifest; + } catch (error) { + throw Error( + "[Disconnected Export] Please make sure you've started the disconnected proxy `npm run start:disconnected-proxy`" + ); + } + }; + + const graphqlSitemapService = new GraphQLSitemapService({ + endpoint: config.graphQLEndpoint, + apiKey: config.sitecoreApiKey, + siteName: config.jssAppName, + /* + The Sitemap Service needs a root item ID in order to fetch the list of pages for the current + app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; + otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. + rootItemId: '{GUID}' + */ + }); + + const disconnectedSitemapService = new DisconnectedSitemapService( + (getManifest() as unknown) as ManifestInstance + ); + + /** + * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) + * @param {GetStaticPathsContext} context + */ + const fetch: SitemapFetchFunction = (context) => { + // If we are in Export/Disconnected mode + if (process.env.EXPORT_MODE) { + return process.env.JSS_MODE === 'disconnected' + ? disconnectedSitemapService.fetchExportSitemap() + : graphqlSitemapService.fetchExportSitemap(packageConfig.language); + } + + return graphqlSitemapService.fetchSSGSitemap(context?.locales || []); + }; + + return fetch; + } +} + +export const sitemapFactory = new SitemapFactory(); diff --git a/samples/nextjs/src/lib/sitemap-fetcher.ts b/samples/nextjs/src/lib/sitemap-fetcher.ts deleted file mode 100644 index 93c83fdb95..0000000000 --- a/samples/nextjs/src/lib/sitemap-fetcher.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { - GraphQLSitemapService, - StaticPath, - DisconnectedSitemapService, - ManifestInstance, -} from '@sitecore-jss/sitecore-jss-nextjs'; -import { GetStaticPathsContext } from 'next'; -import config from 'temp/config'; -import { config as packageConfig } from '../../package.json'; - -export class SitecoreSitemapFetcher { - private _graphqlSitemapService: GraphQLSitemapService; - private _disconnectedSitemapService: DisconnectedSitemapService; - - constructor() { - this._graphqlSitemapService = new GraphQLSitemapService({ - endpoint: config.graphQLEndpoint, - apiKey: config.sitecoreApiKey, - siteName: config.jssAppName, - /* - The Sitemap Service needs a root item ID in order to fetch the list of pages for the current - app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; - otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. - rootItemId: '{GUID}' - */ - }); - - this._disconnectedSitemapService = new DisconnectedSitemapService( - (this.getManifest() as unknown) as ManifestInstance - ); - } - - /** - * Get sitecore-import.json manifest - */ - private getManifest() { - if (process.env.JSS_MODE !== 'disconnected') return null; - - try { - const manifest = require('sitecore/manifest/sitecore-import.json'); - - return manifest; - } catch (error) { - throw Error( - "[Disconnected Export] Please make sure you've started the disconnected proxy `npm run start:disconnected-proxy`" - ); - } - } - - /** - * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) - * @param {GetStaticPathsContext} context - */ - async fetch(context?: GetStaticPathsContext): Promise { - // If we are in Export/Disconnected Export mode - if (process.env.EXPORT_MODE) { - return process.env.JSS_MODE === 'disconnected' - ? this._disconnectedSitemapService.fetchExportSitemap() - : this._graphqlSitemapService.fetchExportSitemap(packageConfig.language); - } - - return this._graphqlSitemapService.fetchSSGSitemap(context?.locales || []); - } -} - -export const sitemapFetcher = new SitecoreSitemapFetcher(); diff --git a/samples/nextjs/src/pages/[[...path]].tsx b/samples/nextjs/src/pages/[[...path]].tsx index 1ae2ff5771..cc81f77eb8 100644 --- a/samples/nextjs/src/pages/[[...path]].tsx +++ b/samples/nextjs/src/pages/[[...path]].tsx @@ -11,7 +11,7 @@ import { StyleguideSitecoreContextValue } from 'lib/component-props'; import { SitecorePageProps } from 'lib/page-props'; import { sitecorePagePropsFactory } from 'lib/page-props-factory'; import { componentFactory } from 'temp/componentFactory'; -import { sitemapFetcher } from 'lib/sitemap-fetcher'; +import { sitemapFactory } from 'lib/sitemap-factory'; const SitecorePage = ({ notFound, layoutData, componentProps }: SitecorePageProps): JSX.Element => { useEffect(() => { @@ -55,7 +55,9 @@ export const getStaticPaths: GetStaticPaths = async (context) => { if (process.env.NODE_ENV !== 'development') { // Note: Next.js runs export in production mode - const paths = await sitemapFetcher.fetch(context); + const sitemapFetcher = sitemapFactory.create(); + + const paths = await sitemapFetcher(context); return { paths, From 54dc98076b6130c9336cb79641a0d709281a9e3f Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 21 Jul 2021 10:20:25 +0300 Subject: [PATCH 02/20] Add log --- samples/nextjs/jss-create.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index 859d08964f..e8ad23ec4b 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -56,6 +56,8 @@ function getPath(filepath) { function cleanUpStrippedFiles() { const files = getStrippedFiles(); + console.log(chalk.cyan('Removing stripped files...')); + files.forEach(filepath => fs.unlinkSync(filepath)) } From 8cf9b3813af1a74b7bdebe5ed603677417f00eb6 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 13:53:03 +0300 Subject: [PATCH 03/20] Add strip script --- docs/data/routes/docs/fundamentals/cli/en.md | 2 +- samples/nextjs/jss-create.js | 30 +----- samples/nextjs/next.config.base.js | 71 +++++++------- samples/nextjs/next.config.base.stripped.js | 89 ------------------ samples/nextjs/scripts/bootstrap.stripped.ts | 20 ---- samples/nextjs/scripts/bootstrap.ts | 9 +- samples/nextjs/scripts/scaffold-component.ts | 9 ++ samples/nextjs/scripts/strip.js | 92 +++++++++++++++++++ .../graphql/GraphQL-ConnectedDemo.dynamic.tsx | 8 +- .../src/components/graphql/GraphQL-Layout.tsx | 2 + .../styleguide/Styleguide-Tracking.tsx | 2 + .../src/lib/sitemap-factory.stripped.ts | 42 --------- samples/nextjs/src/lib/sitemap-factory.ts | 70 -------------- samples/nextjs/src/lib/sitemap-fetcher.ts | 79 ++++++++++++++++ samples/nextjs/src/pages/[[...path]].tsx | 6 +- 15 files changed, 241 insertions(+), 290 deletions(-) delete mode 100644 samples/nextjs/next.config.base.stripped.js delete mode 100644 samples/nextjs/scripts/bootstrap.stripped.ts create mode 100644 samples/nextjs/scripts/strip.js delete mode 100644 samples/nextjs/src/lib/sitemap-factory.stripped.ts delete mode 100644 samples/nextjs/src/lib/sitemap-factory.ts create mode 100644 samples/nextjs/src/lib/sitemap-fetcher.ts diff --git a/docs/data/routes/docs/fundamentals/cli/en.md b/docs/data/routes/docs/fundamentals/cli/en.md index c511c170fc..29ca6f7393 100644 --- a/docs/data/routes/docs/fundamentals/cli/en.md +++ b/docs/data/routes/docs/fundamentals/cli/en.md @@ -39,7 +39,7 @@ jss create |`--proxy`, `-p` | Specifies a HTTP proxy when downloading templates. | A local directory path | - | all | |`--fetchWith` | Specifies how the applicaiton should fetch Sitecore layout and dictionary data. |`REST` or `GraphQL` | `REST` | nextjs | |`--prerender` | Specifies the Next.js pre-rendering form for the primary `[[...path]].tsx` route. | `SSG` or `SSR` | `SSG` | nextjs | -|`--empty` | Specifies the Next.js app should be created empty | - | `false` | nextjs | +|`--empty` | Specifies the Next.js app should be created empty | - | `false` | all | **Examples** ``` diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index e8ad23ec4b..4ac916890c 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -3,6 +3,7 @@ const path = require('path'); const chalk = require('chalk'); const { applyNameToProject } = require('@sitecore-jss/sitecore-jss-cli/dist/create'); const { execSync } = require('child_process'); +const strip = require('./scripts/strip'); /** * This function is invoked by `jss create` when an app based on this template is created. @@ -37,8 +38,6 @@ module.exports = function createJssProject(argv, nextSteps) { if (argv.empty) { createEmptyStarter(); - } else { - cleanUpStrippedFiles(); } setFetchWith(argv.fetchWith); @@ -53,23 +52,6 @@ function getPath(filepath) { return path.join(__dirname, filepath); } -function cleanUpStrippedFiles() { - const files = getStrippedFiles(); - - console.log(chalk.cyan('Removing stripped files...')); - - files.forEach(filepath => fs.unlinkSync(filepath)) -} - -function getStrippedFiles() { - return [ - getPath('scripts/bootstrap.stripped.ts'), - getPath('scripts/scaffold-component.stripped.ts'), - getPath('src/lib/sitemap-factory.stripped.ts'), - getPath('next.config.base.stripped.js'), - ]; -} - function createEmptyStarter() { const dataDir = getPath('data'); const disconnectedProxyScript = getPath('scripts/disconnected-mode-proxy.ts'); @@ -79,20 +61,14 @@ function createEmptyStarter() { console.log(chalk.cyan('Cleaning up the sample...')); - const strippedFiles = getStrippedFiles(); - - strippedFiles.forEach(strippedFilePath => { - const defaultFilePath = strippedFilePath.replace('.stripped', ''); - fs.unlinkSync(defaultFilePath) - fs.renameSync(strippedFilePath, defaultFilePath); - }) - fs.rmdirSync(dataDir, { recursive: true }); fs.unlinkSync(disconnectedProxyScript); fs.unlinkSync(manifestTemplate); fs.rmdirSync(definitionsDir, { recursive: true }); fs.rmdirSync(componentsDir, { recursive: true }); + strip(); + const packageJson = require('./package.json'); delete packageJson.scripts.start; diff --git a/samples/nextjs/next.config.base.js b/samples/nextjs/next.config.base.js index 8ba4c658c2..6d70fa429e 100644 --- a/samples/nextjs/next.config.base.js +++ b/samples/nextjs/next.config.base.js @@ -1,10 +1,16 @@ const jssConfig = require('./src/temp/config'); const packageConfig = require('./package.json').config; -const { constants, getPublicUrl } = require('@sitecore-jss/sitecore-jss-nextjs'); +const { + // #START_STRIP + constants, + // #END_STRIP + getPublicUrl, +} = require('@sitecore-jss/sitecore-jss-nextjs'); +// #START_STRIP const disconnectedServerUrl = `http://localhost:${process.env.PROXY_PORT || 3042}/`; const isDisconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; - +// #END_STRIP const publicUrl = getPublicUrl(); const nextConfig = { @@ -29,6 +35,7 @@ const nextConfig = { }, async rewrites() { + // #START_STRIP if (isDisconnected) { // When disconnected we proxy to the local faux layout service host, see scripts/disconnected-mode-server.js return [ @@ -50,37 +57,37 @@ const nextConfig = { destination: `${disconnectedServerUrl}/data/media/:path*`, }, ]; - } else { - // When in connected mode we want to proxy Sitecore paths off to Sitecore - return [ - { - source: '/sitecore/:path*', - destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, - }, - { - source: '/:locale/sitecore/:path*', - destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, - }, - // media items - { - source: '/-/:path*', - destination: `${jssConfig.sitecoreApiHost}/-/:path*`, - }, - { - source: '/:locale/-/:path*', - destination: `${jssConfig.sitecoreApiHost}/-/:path*`, - }, - // visitor identification - { - source: '/layouts/:path*', - destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, - }, - { - source: '/:locale/layouts/:path*', - destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, - }, - ]; } + // #END_STRIP + // When in connected mode we want to proxy Sitecore paths off to Sitecore + return [ + { + source: '/sitecore/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, + }, + { + source: '/:locale/sitecore/:path*', + destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, + }, + // media items + { + source: '/-/:path*', + destination: `${jssConfig.sitecoreApiHost}/-/:path*`, + }, + { + source: '/:locale/-/:path*', + destination: `${jssConfig.sitecoreApiHost}/-/:path*`, + }, + // visitor identification + { + source: '/layouts/:path*', + destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, + }, + { + source: '/:locale/layouts/:path*', + destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, + }, + ]; }, webpack: (config, options) => { diff --git a/samples/nextjs/next.config.base.stripped.js b/samples/nextjs/next.config.base.stripped.js deleted file mode 100644 index c30f57315d..0000000000 --- a/samples/nextjs/next.config.base.stripped.js +++ /dev/null @@ -1,89 +0,0 @@ -const jssConfig = require('./src/temp/config'); -const packageConfig = require('./package.json').config; -const { getPublicUrl } = require('@sitecore-jss/sitecore-jss-nextjs'); - -const publicUrl = getPublicUrl(); - -const nextConfig = { - // Set assetPrefix to our public URL - assetPrefix: publicUrl, - - // Allow specifying a distinct distDir when concurrently running app in a container - distDir: process.env.NEXTJS_DIST_DIR || '.next', - - // Make the same PUBLIC_URL available as an environment variable on the client bundle - env: { - PUBLIC_URL: publicUrl, - }, - - i18n: { - // These are all the locales you want to support in your application. - // These should generally match (or at least be a subset of) those in Sitecore. - locales: ['en', 'da-DK'], - // This is the locale that will be used when visiting a non-locale - // prefixed path e.g. `/styleguide`. - defaultLocale: packageConfig.language, - }, - - async rewrites() { - // When in connected mode we want to proxy Sitecore paths off to Sitecore - return [ - { - source: '/sitecore/:path*', - destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, - }, - { - source: '/:locale/sitecore/:path*', - destination: `${jssConfig.sitecoreApiHost}/sitecore/:path*`, - }, - // media items - { - source: '/-/:path*', - destination: `${jssConfig.sitecoreApiHost}/-/:path*`, - }, - { - source: '/:locale/-/:path*', - destination: `${jssConfig.sitecoreApiHost}/-/:path*`, - }, - // visitor identification - { - source: '/layouts/:path*', - destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, - }, - { - source: '/:locale/layouts/:path*', - destination: `${jssConfig.sitecoreApiHost}/layouts/:path*`, - }, - ]; - }, - - webpack: (config, options) => { - applyGraphQLCodeGenerationLoaders(config, options); - - return config; - }, -}; - -const applyGraphQLCodeGenerationLoaders = (config, options) => { - config.module.rules.push({ - test: /\.graphql$/, - exclude: /node_modules/, - use: [options.defaultLoaders.babel, { loader: 'graphql-let/loader' }], - }); - - config.module.rules.push({ - test: /\.graphqls$/, - exclude: /node_modules/, - use: ['graphql-let/schema/loader'], - }); - - config.module.rules.push({ - test: /\.ya?ml$/, - type: 'json', - use: 'yaml-loader', - }); - - return config; -}; - -module.exports = nextConfig; diff --git a/samples/nextjs/scripts/bootstrap.stripped.ts b/samples/nextjs/scripts/bootstrap.stripped.ts deleted file mode 100644 index 596b68f218..0000000000 --- a/samples/nextjs/scripts/bootstrap.stripped.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { generateConfig } from './generate-config'; - -/* - BOOTSTRAPPING - The bootstrap process runs before build, and generates JS that needs to be - included into the build - specifically, the component name to component mapping, - and the global config module. -*/ - -/* - CONFIG GENERATION - Generates the /src/temp/config.js file which contains runtime configuration - that the app can import and use. -*/ -generateConfig(); - -/* - COMPONENT FACTORY GENERATION -*/ -import './generate-component-factory'; diff --git a/samples/nextjs/scripts/bootstrap.ts b/samples/nextjs/scripts/bootstrap.ts index 8bde2575b7..cab3af6395 100644 --- a/samples/nextjs/scripts/bootstrap.ts +++ b/samples/nextjs/scripts/bootstrap.ts @@ -1,6 +1,7 @@ import { generateConfig } from './generate-config'; +// #START_STRIP import { constants } from '@sitecore-jss/sitecore-jss-nextjs'; - +// #END_STRIP /* BOOTSTRAPPING The bootstrap process runs before build, and generates JS that needs to be @@ -8,16 +9,18 @@ import { constants } from '@sitecore-jss/sitecore-jss-nextjs'; and the global config module. */ +// #START_STRIP const disconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; - +// #END_STRIP /* CONFIG GENERATION Generates the /src/temp/config.js file which contains runtime configuration that the app can import and use. */ +// #START_STRIP const port = process.env.PORT || 3000; const configOverride = disconnected ? { sitecoreApiHost: `http://localhost:${port}` } : undefined; - +// #END_STRIP generateConfig(configOverride); /* diff --git a/samples/nextjs/scripts/scaffold-component.ts b/samples/nextjs/scripts/scaffold-component.ts index 2d74eaac9b..2173189b6a 100644 --- a/samples/nextjs/scripts/scaffold-component.ts +++ b/samples/nextjs/scripts/scaffold-component.ts @@ -18,9 +18,12 @@ import fs from 'fs'; import path from 'path'; import chalk from 'chalk'; import generateComponentSrc from './templates/component-src'; +// #START_STRIP import generateComponentManifest from './templates/component-manifest'; const componentManifestDefinitionsPath = 'sitecore/definitions/components'; +// #END_STRIP + const componentRootPath = 'src/components'; // Matches component names that start with a capital letter, and contain only letters, number, @@ -49,6 +52,7 @@ const componentOutputPath = scaffoldFile( filename ); +// #START_STRIP let manifestOutputPath = null; if (fs.existsSync(componentManifestDefinitionsPath)) { const filename = `${componentName}.sitecore.ts`; @@ -64,6 +68,7 @@ if (fs.existsSync(componentManifestDefinitionsPath)) { did not exist. This is normal for Sitecore-first workflow.`) ); } +// #END_STRIP console.log( chalk.green(` @@ -71,6 +76,7 @@ Scaffolding of ${componentName} complete. Next steps:`) ); +// #START_STRIP if (manifestOutputPath) { console.log(`* Define the component's data in ${chalk.green(manifestOutputPath)}`); } else { @@ -80,9 +86,11 @@ if (manifestOutputPath) { )}, or create the rendering item and datasource template yourself.` ); } +// #END_STRIP if (componentOutputPath) { console.log(`* Implement the React component in ${chalk.green(componentOutputPath)}`); } +// #START_STRIP if (manifestOutputPath) { console.log( `* Add the component to a route layout (/data/routes) and test it with ${chalk.green( @@ -97,6 +105,7 @@ if (manifestOutputPath) { ); console.log(`* Add the component to a route using Sitecore Experience Editor, and test it.`); } +// #END_STRIP /** * Force to use `crlf` line endings, we are using `crlf` across the project. diff --git a/samples/nextjs/scripts/strip.js b/samples/nextjs/scripts/strip.js new file mode 100644 index 0000000000..4c7dd3370d --- /dev/null +++ b/samples/nextjs/scripts/strip.js @@ -0,0 +1,92 @@ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|\.next|out|\.generated)(\\|$)/gi; +const INCLUDE_FILE_REGEXP = /\.(js|ts)$/g; + +const START_REGEXP = /\/\/ #START_STRIP/g; +const END_REGEXP = /\/\/ #END_STRIP$/; + +/** + * Remove part of code which inside the STRIP block + * @param {string} file + */ +const compile = (file) => { + const content = fs.readFileSync(file, 'utf8'); + + let shouldRemove = false; + + const lines = content.split(os.EOL).filter(line => { + if (START_REGEXP.test(line)) { + shouldRemove = true; + } + + if (END_REGEXP.test(line)) { + shouldRemove = false; + + return shouldRemove; + } + + return !shouldRemove; + }); + + fs.writeFileSync(file, lines.join('\r\n')); +}; + +/** + * Iterate files/directories in provided directory + * @param {string} dir current directory + * @param {Function} done called when all files are iterated in directory + */ +const iterate = function (dir, done) { + let results = []; + + try { + const list = fs.readdirSync(dir); + + let i = 0; + + const nextFile = () => { + let file = list[i++]; + + if (EXCLUDE_DIR_REGEXP.test(file)) { + return nextFile(); + } + + if (!file) return done(null, results); + + file = path.resolve(dir, file); + + const stat = fs.statSync(file); + + if (stat && stat.isDirectory()) { + iterate(file, (err, res) => { + results = results.concat(res); + nextFile(); + }); + + return; + } + + if (!INCLUDE_FILE_REGEXP.test(file)) return nextFile(); + + compile(file); + + results.push(file); + + nextFile(); + }; + + nextFile(); + } catch (error) { + done(error); + } +}; + +module.exports = () => { + iterate(path.resolve(__dirname, '..'), (err, results) => { + if (err) throw err; + console.log(results); + }); +} diff --git a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx index 301349d2fa..25bf22c335 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx @@ -5,7 +5,9 @@ import { GetServerSideComponentProps, GetStaticComponentProps, useComponentProps, + // #START_STRIP constants, + // #END_STRIP GraphQLRequestClient, withDatasourceCheck, resetEditorChromes, @@ -106,10 +108,11 @@ const GraphQLConnectedDemo = (props: StyleguideComponentProps): JSX.Element => { * @param {GetStaticPropsContext} context */ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData) => { + // #START_STRIP if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } - + // #END_STRIP const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); @@ -130,10 +133,11 @@ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutD * @param {GetServerSidePropsContext} context */ export const getServerSideProps: GetServerSideComponentProps = async (rendering, layoutData) => { + // #START_STRIP if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } - + // #END_STRIP const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); diff --git a/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx b/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx index e3ec4fcc4c..ff3e383975 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx @@ -9,6 +9,7 @@ const GraphQLLayout = ({ rendering }: StyleguideComponentProps): JSX.Element => return (
    + {/* // #START_STRIP */} {disconnectedMode && ( <>

    @@ -27,6 +28,7 @@ const GraphQLLayout = ({ rendering }: StyleguideComponentProps): JSX.Element =>

    )} + {/* // #END_STRIP */} {!disconnectedMode && }
    ); diff --git a/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx b/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx index b1617049cd..f525df9178 100644 --- a/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx +++ b/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx @@ -153,9 +153,11 @@ class StyleguideTracking extends React.Component { return ( + {/* // #START_STRIP */} {disconnectedMode && (

    The tracking API is only available in connected, integrated, or headless modes.

    )} + {/* // #END_STRIP */} {!disconnectedMode && (

    diff --git a/samples/nextjs/src/lib/sitemap-factory.stripped.ts b/samples/nextjs/src/lib/sitemap-factory.stripped.ts deleted file mode 100644 index a23162285e..0000000000 --- a/samples/nextjs/src/lib/sitemap-factory.stripped.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { GraphQLSitemapService, StaticPath } from '@sitecore-jss/sitecore-jss-nextjs'; -import { GetStaticPathsContext } from 'next'; -import config from 'temp/config'; -import { config as packageConfig } from '../../package.json'; - -type SitemapFetchFunction = ( - context?: GetStaticPathsContext -) => Promise | StaticPath[]; - -export class SitemapFactory { - create(): SitemapFetchFunction { - const graphqlSitemapService = new GraphQLSitemapService({ - endpoint: config.graphQLEndpoint, - apiKey: config.sitecoreApiKey, - siteName: config.jssAppName, - /* - The Sitemap Service needs a root item ID in order to fetch the list of pages for the current - app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; - otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. - rootItemId: '{GUID}' - */ - }); - - /** - * Generates SitecoreSitemap for given mode (Export / SSG) - * @param {GetStaticPathsContext} context - */ - const fetch: SitemapFetchFunction = (context) => { - // If we are in Export xport mode - if (process.env.EXPORT_MODE) { - return graphqlSitemapService.fetchExportSitemap(packageConfig.language); - } - - return graphqlSitemapService.fetchSSGSitemap(context?.locales || []); - }; - - return fetch; - } -} - -export const sitemapFactory = new SitemapFactory(); diff --git a/samples/nextjs/src/lib/sitemap-factory.ts b/samples/nextjs/src/lib/sitemap-factory.ts deleted file mode 100644 index 9c1abdfb45..0000000000 --- a/samples/nextjs/src/lib/sitemap-factory.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -import { - GraphQLSitemapService, - StaticPath, - DisconnectedSitemapService, - ManifestInstance, -} from '@sitecore-jss/sitecore-jss-nextjs'; -import { GetStaticPathsContext } from 'next'; -import config from 'temp/config'; -import { config as packageConfig } from '../../package.json'; - -type SitemapFetchFunction = ( - context?: GetStaticPathsContext -) => Promise | StaticPath[]; - -export class SitemapFactory { - create(): SitemapFetchFunction { - /** - * Get sitecore-import.json manifest - */ - const getManifest = () => { - if (process.env.JSS_MODE !== 'disconnected') return null; - - try { - const manifest = require('sitecore/manifest/sitecore-import.json'); - - return manifest; - } catch (error) { - throw Error( - "[Disconnected Export] Please make sure you've started the disconnected proxy `npm run start:disconnected-proxy`" - ); - } - }; - - const graphqlSitemapService = new GraphQLSitemapService({ - endpoint: config.graphQLEndpoint, - apiKey: config.sitecoreApiKey, - siteName: config.jssAppName, - /* - The Sitemap Service needs a root item ID in order to fetch the list of pages for the current - app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; - otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. - rootItemId: '{GUID}' - */ - }); - - const disconnectedSitemapService = new DisconnectedSitemapService( - (getManifest() as unknown) as ManifestInstance - ); - - /** - * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) - * @param {GetStaticPathsContext} context - */ - const fetch: SitemapFetchFunction = (context) => { - // If we are in Export/Disconnected mode - if (process.env.EXPORT_MODE) { - return process.env.JSS_MODE === 'disconnected' - ? disconnectedSitemapService.fetchExportSitemap() - : graphqlSitemapService.fetchExportSitemap(packageConfig.language); - } - - return graphqlSitemapService.fetchSSGSitemap(context?.locales || []); - }; - - return fetch; - } -} - -export const sitemapFactory = new SitemapFactory(); diff --git a/samples/nextjs/src/lib/sitemap-fetcher.ts b/samples/nextjs/src/lib/sitemap-fetcher.ts new file mode 100644 index 0000000000..63131fe63b --- /dev/null +++ b/samples/nextjs/src/lib/sitemap-fetcher.ts @@ -0,0 +1,79 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { + GraphQLSitemapService, + StaticPath, + // #START_STRIP + DisconnectedSitemapService, + ManifestInstance, + // #END_STRIP +} from '@sitecore-jss/sitecore-jss-nextjs'; +import { GetStaticPathsContext } from 'next'; +import config from 'temp/config'; +import { config as packageConfig } from '../../package.json'; + +export class SitecoreSitemapFetcher { + private _graphqlSitemapService: GraphQLSitemapService; + // #START_STRIP + private _disconnectedSitemapService: DisconnectedSitemapService; + // #END_STRIP + + constructor() { + this._graphqlSitemapService = new GraphQLSitemapService({ + endpoint: config.graphQLEndpoint, + apiKey: config.sitecoreApiKey, + siteName: config.jssAppName, + /* + The Sitemap Service needs a root item ID in order to fetch the list of pages for the current + app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here; + otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name. + rootItemId: '{GUID}' + */ + }); + + // #START_STRIP + this._disconnectedSitemapService = new DisconnectedSitemapService( + (this.getManifest() as unknown) as ManifestInstance + ); + // #END_STRIP + } + + // #START_STRIP + /** + * Get sitecore-import.json manifest + */ + private getManifest() { + if (process.env.JSS_MODE !== 'disconnected') return null; + + try { + const manifest = require('sitecore/manifest/sitecore-import.json'); + + return manifest; + } catch (error) { + throw Error( + "[Disconnected Export] Please make sure you've started the disconnected proxy `npm run start:disconnected-proxy`" + ); + } + } + // #END_STRIP + + /** + * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) + * @param {GetStaticPathsContext} context + */ + async fetch(context?: GetStaticPathsContext): Promise { + // If we are in Export mode + if (process.env.EXPORT_MODE) { + // #START_STRIP + // Disconnected Export mode + if (process.env.JSS_MODE === 'disconnected') { + return this._disconnectedSitemapService.fetchExportSitemap(); + } + // #END_STRIP + return this._graphqlSitemapService.fetchExportSitemap(packageConfig.language); + } + + return this._graphqlSitemapService.fetchSSGSitemap(context?.locales || []); + } +} + +export const sitemapFetcher = new SitecoreSitemapFetcher(); diff --git a/samples/nextjs/src/pages/[[...path]].tsx b/samples/nextjs/src/pages/[[...path]].tsx index cc81f77eb8..1ae2ff5771 100644 --- a/samples/nextjs/src/pages/[[...path]].tsx +++ b/samples/nextjs/src/pages/[[...path]].tsx @@ -11,7 +11,7 @@ import { StyleguideSitecoreContextValue } from 'lib/component-props'; import { SitecorePageProps } from 'lib/page-props'; import { sitecorePagePropsFactory } from 'lib/page-props-factory'; import { componentFactory } from 'temp/componentFactory'; -import { sitemapFactory } from 'lib/sitemap-factory'; +import { sitemapFetcher } from 'lib/sitemap-fetcher'; const SitecorePage = ({ notFound, layoutData, componentProps }: SitecorePageProps): JSX.Element => { useEffect(() => { @@ -55,9 +55,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => { if (process.env.NODE_ENV !== 'development') { // Note: Next.js runs export in production mode - const sitemapFetcher = sitemapFactory.create(); - - const paths = await sitemapFetcher(context); + const paths = await sitemapFetcher.fetch(context); return { paths, From e1c3ac7d4b05c45cbe54f93bdca23b9dc44a6949 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 14:29:19 +0300 Subject: [PATCH 04/20] changes --- samples/nextjs/scripts/bootstrap.ts | 8 +- .../scripts/scaffold-component.stripped.ts | 97 ------------------- samples/nextjs/scripts/scaffold-component.ts | 1 - samples/nextjs/src/lib/sitemap-fetcher.ts | 14 +-- 4 files changed, 11 insertions(+), 109 deletions(-) delete mode 100644 samples/nextjs/scripts/scaffold-component.stripped.ts diff --git a/samples/nextjs/scripts/bootstrap.ts b/samples/nextjs/scripts/bootstrap.ts index cab3af6395..d011fd0cc3 100644 --- a/samples/nextjs/scripts/bootstrap.ts +++ b/samples/nextjs/scripts/bootstrap.ts @@ -19,8 +19,14 @@ const disconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; */ // #START_STRIP const port = process.env.PORT || 3000; -const configOverride = disconnected ? { sitecoreApiHost: `http://localhost:${port}` } : undefined; // #END_STRIP +let configOverride = undefined; +// #START_STRIP +if (disconnected) { + configOverride = { sitecoreApiHost: `http://localhost:${port}` }; +} +// #END_STRIP + generateConfig(configOverride); /* diff --git a/samples/nextjs/scripts/scaffold-component.stripped.ts b/samples/nextjs/scripts/scaffold-component.stripped.ts deleted file mode 100644 index 012317efe9..0000000000 --- a/samples/nextjs/scripts/scaffold-component.stripped.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - Component Scaffolding Script - This is a script that enables scaffolding a new JSS component using `jss scaffold `. - The default convention is that component names must start with a capital letter, and can contain - letters, number, underscores, or dashes. - - If the parameter includes a path, it must be relative to the src/components folder. - For example, `jss scaffold search/SearchBox` will create a component called `SearchBox` in - `src/components/search/SearchBox.tsx`. Specifying a relative path is optional, and just providing - the name is ok. - - Edit this script if you wish to use your own conventions for component storage in your JSS app. -*/ - -/* eslint-disable no-throw-literal,no-console */ - -import fs from 'fs'; -import path from 'path'; -import chalk from 'chalk'; -import generateComponentSrc from './templates/component-src'; - -const componentRootPath = 'src/components'; - -// Matches component names that start with a capital letter, and contain only letters, number, -// underscores, or dashes. Optionally, the component name can be preceded by a relative path -const nameParamFormat = new RegExp(/^((?:[\w-]+\/)*)([A-Z][\w-]+)$/); -const componentArg = process.argv[2]; - -if (!componentArg) { - throw 'Component name was not passed. Usage: jss scaffold '; -} - -const regExResult = nameParamFormat.exec(componentArg); - -if (regExResult === null) { - throw `Component name should start with an uppercase letter and contain only letters, numbers, -dashes, or underscores. If specifying a path, it must be relative to src/components`; -} - -const componentPath = regExResult[1]; -const componentName = regExResult[2]; -const filename = `${componentName}.tsx`; - -const componentOutputPath = scaffoldFile( - componentRootPath, - generateComponentSrc(componentName), - filename -); - -console.log( - chalk.green(` -Scaffolding of ${componentName} complete. -Next steps:`) -); - -console.log( - `* Scaffold the component in Sitecore using '${chalk.green( - `jss deploy component ${componentName} --allowedPlaceholders placeholder-for-component` - )}, or create the rendering item and datasource template yourself.` -); - -if (componentOutputPath) { - console.log(`* Implement the React component in ${chalk.green(componentOutputPath)}`); -} - -console.log(`* Add the component to a route using Sitecore Experience Editor, and test it.`); - -/** - * Force to use `crlf` line endings, we are using `crlf` across the project. - * Replace: `lf` (\n), `cr` (\r) - * @param {string} content - */ -function editLineEndings(content: string) { - return content.replace(/\r|\n/gm, '\r\n'); -} - -/** - * Creates a file relative to the specified path if the file doesn't exist. Creates directories as needed. - * @param {string} rootPath - the root path - * @param {string} fileContent - the file content - * @param {string} filename - the filename - * @returns the new file's filepath - */ -function scaffoldFile(rootPath: string, fileContent: string, filename: string): string | null { - const outputDir = path.join(rootPath, componentPath); - const outputFile = path.join(outputDir, filename); - - if (fs.existsSync(outputFile)) { - console.log(chalk.red(`Skipping creating ${outputFile}; already exists.`)); - return null; - } - - fs.mkdirSync(outputDir, { recursive: true }); - fs.writeFileSync(outputFile, editLineEndings(fileContent), 'utf8'); - console.log(chalk.green(`File ${outputFile} has been scaffolded.`)); - return outputFile; -} diff --git a/samples/nextjs/scripts/scaffold-component.ts b/samples/nextjs/scripts/scaffold-component.ts index 2173189b6a..4063a5af2f 100644 --- a/samples/nextjs/scripts/scaffold-component.ts +++ b/samples/nextjs/scripts/scaffold-component.ts @@ -69,7 +69,6 @@ did not exist. This is normal for Sitecore-first workflow.`) ); } // #END_STRIP - console.log( chalk.green(` Scaffolding of ${componentName} complete. diff --git a/samples/nextjs/src/lib/sitemap-fetcher.ts b/samples/nextjs/src/lib/sitemap-fetcher.ts index 63131fe63b..27440b9d36 100644 --- a/samples/nextjs/src/lib/sitemap-fetcher.ts +++ b/samples/nextjs/src/lib/sitemap-fetcher.ts @@ -1,12 +1,8 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -import { - GraphQLSitemapService, - StaticPath, - // #START_STRIP - DisconnectedSitemapService, - ManifestInstance, - // #END_STRIP -} from '@sitecore-jss/sitecore-jss-nextjs'; +import { GraphQLSitemapService, StaticPath } from '@sitecore-jss/sitecore-jss-nextjs'; +// #START_STRIP +import { DisconnectedSitemapService, ManifestInstance } from '@sitecore-jss/sitecore-jss-nextjs'; +// #END_STRIP import { GetStaticPathsContext } from 'next'; import config from 'temp/config'; import { config as packageConfig } from '../../package.json'; @@ -29,7 +25,6 @@ export class SitecoreSitemapFetcher { rootItemId: '{GUID}' */ }); - // #START_STRIP this._disconnectedSitemapService = new DisconnectedSitemapService( (this.getManifest() as unknown) as ManifestInstance @@ -55,7 +50,6 @@ export class SitecoreSitemapFetcher { } } // #END_STRIP - /** * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) * @param {GetStaticPathsContext} context From cc135f665551c6f5ced5f2fdde0205cd8835da4c Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 14:33:00 +0300 Subject: [PATCH 05/20] changes --- samples/nextjs/scripts/bootstrap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/nextjs/scripts/bootstrap.ts b/samples/nextjs/scripts/bootstrap.ts index d011fd0cc3..d3d8cd0e2c 100644 --- a/samples/nextjs/scripts/bootstrap.ts +++ b/samples/nextjs/scripts/bootstrap.ts @@ -20,10 +20,10 @@ const disconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; // #START_STRIP const port = process.env.PORT || 3000; // #END_STRIP -let configOverride = undefined; +const configOverride: { [key: string]: string } = {}; // #START_STRIP if (disconnected) { - configOverride = { sitecoreApiHost: `http://localhost:${port}` }; + configOverride.sitecoreApiHost = `http://localhost:${port}`; } // #END_STRIP From a43cbc41691dec55c66e13b7fb474bcbbb6ddfd5 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 14:56:50 +0300 Subject: [PATCH 06/20] Allow tsx --- samples/nextjs/scripts/strip.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/nextjs/scripts/strip.js b/samples/nextjs/scripts/strip.js index 4c7dd3370d..8e8377fce2 100644 --- a/samples/nextjs/scripts/strip.js +++ b/samples/nextjs/scripts/strip.js @@ -3,10 +3,10 @@ const os = require('os'); const path = require('path'); const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|\.next|out|\.generated)(\\|$)/gi; -const INCLUDE_FILE_REGEXP = /\.(js|ts)$/g; +const INCLUDE_FILE_REGEXP = /\.(js|ts?x)$/g; const START_REGEXP = /\/\/ #START_STRIP/g; -const END_REGEXP = /\/\/ #END_STRIP$/; +const END_REGEXP = /\/\/ #END_STRIP/; /** * Remove part of code which inside the STRIP block From 696b4b080c8adff23068615c5f659554e3279567 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 14:57:55 +0300 Subject: [PATCH 07/20] remove strip script at the end of jss-create --- samples/nextjs/jss-create.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index 4ac916890c..dc82a38ca3 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -56,6 +56,7 @@ function createEmptyStarter() { const dataDir = getPath('data'); const disconnectedProxyScript = getPath('scripts/disconnected-mode-proxy.ts'); const manifestTemplate = getPath('scripts/templates/component-manifest.ts'); + const stripScript = getPath('scripts/strip.js'); const definitionsDir = getPath('sitecore/definitions'); const componentsDir = getPath('src/components'); @@ -69,6 +70,8 @@ function createEmptyStarter() { strip(); + fs.unlinkSync(stripScript); + const packageJson = require('./package.json'); delete packageJson.scripts.start; From 7eb34d2bf37fe289b9bc3fbcecdc5d70efe0ab3a Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 15:51:10 +0300 Subject: [PATCH 08/20] Changes --- samples/nextjs/jss-create.js | 4 ++++ samples/nextjs/scripts/strip.js | 17 +++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index dc82a38ca3..392b427bf4 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -70,6 +70,7 @@ function createEmptyStarter() { strip(); + fs.mkdirSync(componentsDir); fs.unlinkSync(stripScript); const packageJson = require('./package.json'); @@ -78,6 +79,9 @@ function createEmptyStarter() { delete packageJson.scripts['start:disconnected-proxy']; // Temporary solution until we remove graphql-let, graphql-let can't be used if there are no .graphql files packageJson.scripts.bootstrap = packageJson.scripts.bootstrap.replace(' && graphql-let', ''); + packageJson.scripts.lint = packageJson.scripts.lint + .replace('./sitecore/definitions/**/*.ts ', '') + .replace(' ./data/**/*.yml', ''); fs.writeFileSync(getPath('package.json'), JSON.stringify(packageJson), 'utf8'); } diff --git a/samples/nextjs/scripts/strip.js b/samples/nextjs/scripts/strip.js index 8e8377fce2..a79c55e1db 100644 --- a/samples/nextjs/scripts/strip.js +++ b/samples/nextjs/scripts/strip.js @@ -1,12 +1,11 @@ const fs = require('fs'); -const os = require('os'); const path = require('path'); const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|\.next|out|\.generated)(\\|$)/gi; -const INCLUDE_FILE_REGEXP = /\.(js|ts?x)$/g; +const INCLUDE_FILE_REGEXP = /\.(js|tsx?)$/g; const START_REGEXP = /\/\/ #START_STRIP/g; -const END_REGEXP = /\/\/ #END_STRIP/; +const END_REGEXP = /\/\/ #END_STRIP/g; /** * Remove part of code which inside the STRIP block @@ -17,7 +16,7 @@ const compile = (file) => { let shouldRemove = false; - const lines = content.split(os.EOL).filter(line => { + const lines = content.split('\r\n').filter(line => { if (START_REGEXP.test(line)) { shouldRemove = true; } @@ -50,19 +49,18 @@ const iterate = function (dir, done) { const nextFile = () => { let file = list[i++]; + if (!file) return done(null); + if (EXCLUDE_DIR_REGEXP.test(file)) { return nextFile(); } - if (!file) return done(null, results); - file = path.resolve(dir, file); const stat = fs.statSync(file); if (stat && stat.isDirectory()) { - iterate(file, (err, res) => { - results = results.concat(res); + iterate(file, () => { nextFile(); }); @@ -85,8 +83,7 @@ const iterate = function (dir, done) { }; module.exports = () => { - iterate(path.resolve(__dirname, '..'), (err, results) => { + iterate(path.join(__dirname, '../'), (err) => { if (err) throw err; - console.log(results); }); } From 3f55c32b5b9a49cea03bc662e6d60bcfbf10b290 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 16:01:48 +0300 Subject: [PATCH 09/20] Extend description --- samples/nextjs/jss-create.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index 392b427bf4..7cfe1c7d89 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -32,7 +32,10 @@ module.exports = function createJssProject(argv, nextSteps) { )} : Specifies how Sitecore data (layout, dictionary) is fetched. Default is REST.`, `* ${chalk.green( '--prerender {SSG|SSR}' - )} : Specifies the Next.js pre-rendering form for the optional catch-all route. Default is SSG.` + )} : Specifies the Next.js pre-rendering form for the optional catch-all route. Default is SSG.`, + `* ${chalk.green( + '--empty {true|false}' + )} : Specifies whether the sample should be empty. Disconnected mode and styleguide components will be removed. Default is false.`, ); } From 6d142bef94366cd8905987c9ca80eb8c168173f4 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 16:03:31 +0300 Subject: [PATCH 10/20] Format file --- samples/nextjs/scripts/strip.js | 90 ++++++++++++++++----------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/samples/nextjs/scripts/strip.js b/samples/nextjs/scripts/strip.js index a79c55e1db..72adef7a15 100644 --- a/samples/nextjs/scripts/strip.js +++ b/samples/nextjs/scripts/strip.js @@ -9,14 +9,14 @@ const END_REGEXP = /\/\/ #END_STRIP/g; /** * Remove part of code which inside the STRIP block - * @param {string} file + * @param {string} file */ const compile = (file) => { const content = fs.readFileSync(file, 'utf8'); let shouldRemove = false; - const lines = content.split('\r\n').filter(line => { + const lines = content.split('\r\n').filter((line) => { if (START_REGEXP.test(line)) { shouldRemove = true; } @@ -41,49 +41,49 @@ const compile = (file) => { const iterate = function (dir, done) { let results = []; - try { - const list = fs.readdirSync(dir); - - let i = 0; - - const nextFile = () => { - let file = list[i++]; - - if (!file) return done(null); - - if (EXCLUDE_DIR_REGEXP.test(file)) { - return nextFile(); - } - - file = path.resolve(dir, file); - - const stat = fs.statSync(file); - - if (stat && stat.isDirectory()) { - iterate(file, () => { - nextFile(); - }); - - return; - } - - if (!INCLUDE_FILE_REGEXP.test(file)) return nextFile(); - - compile(file); - - results.push(file); - - nextFile(); - }; - - nextFile(); - } catch (error) { - done(error); - } + try { + const list = fs.readdirSync(dir); + + let i = 0; + + const nextFile = () => { + let file = list[i++]; + + if (!file) return done(null); + + if (EXCLUDE_DIR_REGEXP.test(file)) { + return nextFile(); + } + + file = path.resolve(dir, file); + + const stat = fs.statSync(file); + + if (stat && stat.isDirectory()) { + iterate(file, () => { + nextFile(); + }); + + return; + } + + if (!INCLUDE_FILE_REGEXP.test(file)) return nextFile(); + + compile(file); + + results.push(file); + + nextFile(); + }; + + nextFile(); + } catch (error) { + done(error); + } }; module.exports = () => { - iterate(path.join(__dirname, '../'), (err) => { - if (err) throw err; - }); -} + iterate(path.join(__dirname, '../'), (err) => { + if (err) throw err; + }); +}; From 9f66604648436e0de9fce806dd6437561190d5a0 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 16:07:13 +0300 Subject: [PATCH 11/20] Remove changes --- .../components/graphql/GraphQL-ConnectedDemo.dynamic.tsx | 6 ------ samples/nextjs/src/components/graphql/GraphQL-Layout.tsx | 2 -- .../src/components/styleguide/Styleguide-Tracking.tsx | 2 -- 3 files changed, 10 deletions(-) diff --git a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx index 25bf22c335..be096732fb 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx @@ -5,9 +5,7 @@ import { GetServerSideComponentProps, GetStaticComponentProps, useComponentProps, - // #START_STRIP constants, - // #END_STRIP GraphQLRequestClient, withDatasourceCheck, resetEditorChromes, @@ -108,11 +106,9 @@ const GraphQLConnectedDemo = (props: StyleguideComponentProps): JSX.Element => { * @param {GetStaticPropsContext} context */ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutData) => { - // #START_STRIP if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } - // #END_STRIP const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); @@ -133,11 +129,9 @@ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutD * @param {GetServerSidePropsContext} context */ export const getServerSideProps: GetServerSideComponentProps = async (rendering, layoutData) => { - // #START_STRIP if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } - // #END_STRIP const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); diff --git a/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx b/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx index ff3e383975..e3ec4fcc4c 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-Layout.tsx @@ -9,7 +9,6 @@ const GraphQLLayout = ({ rendering }: StyleguideComponentProps): JSX.Element => return (

    - {/* // #START_STRIP */} {disconnectedMode && ( <>

    @@ -28,7 +27,6 @@ const GraphQLLayout = ({ rendering }: StyleguideComponentProps): JSX.Element =>

    )} - {/* // #END_STRIP */} {!disconnectedMode && }
    ); diff --git a/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx b/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx index f525df9178..b1617049cd 100644 --- a/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx +++ b/samples/nextjs/src/components/styleguide/Styleguide-Tracking.tsx @@ -153,11 +153,9 @@ class StyleguideTracking extends React.Component { return ( - {/* // #START_STRIP */} {disconnectedMode && (

    The tracking API is only available in connected, integrated, or headless modes.

    )} - {/* // #END_STRIP */} {!disconnectedMode && (

    From 9e7a80af0786b17121899f3044eab4e45e5939ac Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 16:08:18 +0300 Subject: [PATCH 12/20] Update doc --- docs/data/routes/docs/fundamentals/cli/en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/routes/docs/fundamentals/cli/en.md b/docs/data/routes/docs/fundamentals/cli/en.md index 29ca6f7393..8ec675abe5 100644 --- a/docs/data/routes/docs/fundamentals/cli/en.md +++ b/docs/data/routes/docs/fundamentals/cli/en.md @@ -39,7 +39,7 @@ jss create |`--proxy`, `-p` | Specifies a HTTP proxy when downloading templates. | A local directory path | - | all | |`--fetchWith` | Specifies how the applicaiton should fetch Sitecore layout and dictionary data. |`REST` or `GraphQL` | `REST` | nextjs | |`--prerender` | Specifies the Next.js pre-rendering form for the primary `[[...path]].tsx` route. | `SSG` or `SSR` | `SSG` | nextjs | -|`--empty` | Specifies the Next.js app should be created empty | - | `false` | all | +|`--empty` | Specifies the app should be created empty | - | `false` | all | **Examples** ``` From bc75bd648f02013a80a8261489daccafabcab8bb Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Mon, 2 Aug 2021 16:12:20 +0300 Subject: [PATCH 13/20] revert --- .../src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx index be096732fb..301349d2fa 100644 --- a/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx +++ b/samples/nextjs/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx @@ -109,6 +109,7 @@ export const getStaticProps: GetStaticComponentProps = async (rendering, layoutD if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } + const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); @@ -132,6 +133,7 @@ export const getServerSideProps: GetServerSideComponentProps = async (rendering, if (process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED) { return null; } + const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, { apiKey: config.sitecoreApiKey, }); From b3d16d2a07fcbdc1a7926b5ca046a9f296358d02 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Tue, 3 Aug 2021 12:06:13 +0300 Subject: [PATCH 14/20] Change `strip` to `empty` suffix by default --- samples/nextjs/next.config.base.js | 12 ++++++------ samples/nextjs/scripts/bootstrap.ts | 16 ++++++++-------- samples/nextjs/scripts/scaffold-component.ts | 16 ++++++++-------- samples/nextjs/src/lib/sitemap-fetcher.ts | 20 ++++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/samples/nextjs/next.config.base.js b/samples/nextjs/next.config.base.js index 6d70fa429e..4ebfcf184f 100644 --- a/samples/nextjs/next.config.base.js +++ b/samples/nextjs/next.config.base.js @@ -1,16 +1,16 @@ const jssConfig = require('./src/temp/config'); const packageConfig = require('./package.json').config; const { - // #START_STRIP + // #START_EMPTY constants, - // #END_STRIP + // #END_EMPTY getPublicUrl, } = require('@sitecore-jss/sitecore-jss-nextjs'); -// #START_STRIP +// #START_EMPTY const disconnectedServerUrl = `http://localhost:${process.env.PROXY_PORT || 3042}/`; const isDisconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; -// #END_STRIP +// #END_EMPTY const publicUrl = getPublicUrl(); const nextConfig = { @@ -35,7 +35,7 @@ const nextConfig = { }, async rewrites() { - // #START_STRIP + // #START_EMPTY if (isDisconnected) { // When disconnected we proxy to the local faux layout service host, see scripts/disconnected-mode-server.js return [ @@ -58,7 +58,7 @@ const nextConfig = { }, ]; } - // #END_STRIP + // #END_EMPTY // When in connected mode we want to proxy Sitecore paths off to Sitecore return [ { diff --git a/samples/nextjs/scripts/bootstrap.ts b/samples/nextjs/scripts/bootstrap.ts index d3d8cd0e2c..fc077e3f3c 100644 --- a/samples/nextjs/scripts/bootstrap.ts +++ b/samples/nextjs/scripts/bootstrap.ts @@ -1,7 +1,7 @@ import { generateConfig } from './generate-config'; -// #START_STRIP +// #START_EMPTY import { constants } from '@sitecore-jss/sitecore-jss-nextjs'; -// #END_STRIP +// #END_EMPTY /* BOOTSTRAPPING The bootstrap process runs before build, and generates JS that needs to be @@ -9,23 +9,23 @@ import { constants } from '@sitecore-jss/sitecore-jss-nextjs'; and the global config module. */ -// #START_STRIP +// #START_EMPTY const disconnected = process.env.JSS_MODE === constants.JSS_MODE.DISCONNECTED; -// #END_STRIP +// #END_EMPTY /* CONFIG GENERATION Generates the /src/temp/config.js file which contains runtime configuration that the app can import and use. */ -// #START_STRIP +// #START_EMPTY const port = process.env.PORT || 3000; -// #END_STRIP +// #END_EMPTY const configOverride: { [key: string]: string } = {}; -// #START_STRIP +// #START_EMPTY if (disconnected) { configOverride.sitecoreApiHost = `http://localhost:${port}`; } -// #END_STRIP +// #END_EMPTY generateConfig(configOverride); diff --git a/samples/nextjs/scripts/scaffold-component.ts b/samples/nextjs/scripts/scaffold-component.ts index 4063a5af2f..2a1cd514b5 100644 --- a/samples/nextjs/scripts/scaffold-component.ts +++ b/samples/nextjs/scripts/scaffold-component.ts @@ -18,11 +18,11 @@ import fs from 'fs'; import path from 'path'; import chalk from 'chalk'; import generateComponentSrc from './templates/component-src'; -// #START_STRIP +// #START_EMPTY import generateComponentManifest from './templates/component-manifest'; const componentManifestDefinitionsPath = 'sitecore/definitions/components'; -// #END_STRIP +// #END_EMPTY const componentRootPath = 'src/components'; @@ -52,7 +52,7 @@ const componentOutputPath = scaffoldFile( filename ); -// #START_STRIP +// #START_EMPTY let manifestOutputPath = null; if (fs.existsSync(componentManifestDefinitionsPath)) { const filename = `${componentName}.sitecore.ts`; @@ -68,14 +68,14 @@ if (fs.existsSync(componentManifestDefinitionsPath)) { did not exist. This is normal for Sitecore-first workflow.`) ); } -// #END_STRIP +// #END_EMPTY console.log( chalk.green(` Scaffolding of ${componentName} complete. Next steps:`) ); -// #START_STRIP +// #START_EMPTY if (manifestOutputPath) { console.log(`* Define the component's data in ${chalk.green(manifestOutputPath)}`); } else { @@ -85,11 +85,11 @@ if (manifestOutputPath) { )}, or create the rendering item and datasource template yourself.` ); } -// #END_STRIP +// #END_EMPTY if (componentOutputPath) { console.log(`* Implement the React component in ${chalk.green(componentOutputPath)}`); } -// #START_STRIP +// #START_EMPTY if (manifestOutputPath) { console.log( `* Add the component to a route layout (/data/routes) and test it with ${chalk.green( @@ -104,7 +104,7 @@ if (manifestOutputPath) { ); console.log(`* Add the component to a route using Sitecore Experience Editor, and test it.`); } -// #END_STRIP +// #END_EMPTY /** * Force to use `crlf` line endings, we are using `crlf` across the project. diff --git a/samples/nextjs/src/lib/sitemap-fetcher.ts b/samples/nextjs/src/lib/sitemap-fetcher.ts index 27440b9d36..054632edcb 100644 --- a/samples/nextjs/src/lib/sitemap-fetcher.ts +++ b/samples/nextjs/src/lib/sitemap-fetcher.ts @@ -1,17 +1,17 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import { GraphQLSitemapService, StaticPath } from '@sitecore-jss/sitecore-jss-nextjs'; -// #START_STRIP +// #START_EMPTY import { DisconnectedSitemapService, ManifestInstance } from '@sitecore-jss/sitecore-jss-nextjs'; -// #END_STRIP +// #END_EMPTY import { GetStaticPathsContext } from 'next'; import config from 'temp/config'; import { config as packageConfig } from '../../package.json'; export class SitecoreSitemapFetcher { private _graphqlSitemapService: GraphQLSitemapService; - // #START_STRIP + // #START_EMPTY private _disconnectedSitemapService: DisconnectedSitemapService; - // #END_STRIP + // #END_EMPTY constructor() { this._graphqlSitemapService = new GraphQLSitemapService({ @@ -25,14 +25,14 @@ export class SitecoreSitemapFetcher { rootItemId: '{GUID}' */ }); - // #START_STRIP + // #START_EMPTY this._disconnectedSitemapService = new DisconnectedSitemapService( (this.getManifest() as unknown) as ManifestInstance ); - // #END_STRIP + // #END_EMPTY } - // #START_STRIP + // #START_EMPTY /** * Get sitecore-import.json manifest */ @@ -49,7 +49,7 @@ export class SitecoreSitemapFetcher { ); } } - // #END_STRIP + // #END_EMPTY /** * Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG) * @param {GetStaticPathsContext} context @@ -57,12 +57,12 @@ export class SitecoreSitemapFetcher { async fetch(context?: GetStaticPathsContext): Promise { // If we are in Export mode if (process.env.EXPORT_MODE) { - // #START_STRIP + // #START_EMPTY // Disconnected Export mode if (process.env.JSS_MODE === 'disconnected') { return this._disconnectedSitemapService.fetchExportSitemap(); } - // #END_STRIP + // #END_EMPTY return this._graphqlSitemapService.fetchExportSitemap(packageConfig.language); } From 6a5927cebf2cbeef9bea193291233a98db49a8ab Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 4 Aug 2021 15:02:38 +0300 Subject: [PATCH 15/20] Add changes --- packages/sitecore-jss-dev-tools/src/index.ts | 1 + .../src/templating/strip.test.ts | 740 ++++++++++++++++++ .../src/templating/strip.ts | 168 ++++ samples/nextjs/jss-create.js | 9 +- samples/nextjs/scripts/strip.js | 89 --- 5 files changed, 912 insertions(+), 95 deletions(-) create mode 100644 packages/sitecore-jss-dev-tools/src/templating/strip.test.ts create mode 100644 packages/sitecore-jss-dev-tools/src/templating/strip.ts delete mode 100644 samples/nextjs/scripts/strip.js diff --git a/packages/sitecore-jss-dev-tools/src/index.ts b/packages/sitecore-jss-dev-tools/src/index.ts index cec5bdb535..4d3854915e 100644 --- a/packages/sitecore-jss-dev-tools/src/index.ts +++ b/packages/sitecore-jss-dev-tools/src/index.ts @@ -28,3 +28,4 @@ export { export { ManifestManager } from './manifest-manager'; export { createDefaultDisconnectedServer } from './disconnected-server/create-default-disconnected-server'; export { ScJssConfig, JssConfiguration, resolveScJssConfig } from './resolve-scjssconfig'; +export { strip } from './templating/strip'; diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts new file mode 100644 index 0000000000..6aa4597ac5 --- /dev/null +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts @@ -0,0 +1,740 @@ +/* eslint-disable no-unused-expressions */ +import { expect } from 'chai'; +import sinon from 'sinon'; +import path from 'path'; +import fs, { Dirent, Stats } from 'fs'; +import * as strip from './strip'; + +describe('strip', () => { + describe('compile', () => { + it('strip only comments using default suffix', () => { + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon + .stub(fs, 'readFileSync') + .returns( + 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\n// #START_JSS\r\nTest99\r\n// #END_JSS' + ); + + strip.compile('test.ts', { stripCode: false }); + + expect(writeFileSyncStub.calledOnce).to.be.true; + expect(writeFileSyncStub.args[0]).to.deep.equal([ + 'test.ts', + 'Test11\r\nTest12 Test13\r\nTest14 Test15\r\nTest2\r\n// #START_JSS\r\nTest99\r\n// #END_JSS', + ]); + + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + + it('strip only comments using custom suffix', () => { + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon + .stub(fs, 'readFileSync') + .returns( + 'Test55\r\n// #START_EMPTY\r\n// #START_JSS\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_JSS\r\n// #END_EMPTY' + ); + + strip.compile('test.ts', { stripCode: false, suffix: 'JSS' }); + + expect(writeFileSyncStub.calledOnce).to.be.true; + expect(writeFileSyncStub.args[0]).to.deep.equal([ + 'test.ts', + 'Test55\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_EMPTY', + ]); + + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + + it('strip code and comments using default suffix', () => { + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon + .stub(fs, 'readFileSync') + .returns( + 'Test11\r\n// #START_JSS\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34\r\n// #END_JSS' + ); + + strip.compile('test.ts', { stripCode: true }); + + expect(writeFileSyncStub.calledOnce).to.be.true; + expect(writeFileSyncStub.args[0]).to.deep.equal([ + 'test.ts', + 'Test11\r\n// #START_JSS\r\nTest2\r\nTest34\r\n// #END_JSS', + ]); + + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + + it('strip code and comments using custom suffix', () => { + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon + .stub(fs, 'readFileSync') + .returns( + 'Test11\r\n// #START_EMPTY\r\n// #START_JSS\r\nTest12 Test13\r\n// #END_JSS\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34' + ); + + strip.compile('test.ts', { stripCode: true, suffix: 'JSS' }); + + expect(writeFileSyncStub.calledOnce).to.be.true; + expect(writeFileSyncStub.args[0]).to.deep.equal([ + 'test.ts', + 'Test11\r\n// #START_EMPTY\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34', + ]); + + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + }); + + describe('processNextFile', () => { + it('process files and directories', () => { + const sourceDirPath = path.resolve(__dirname, 'src/testDir'); + const compileStub = sinon.stub(strip, 'compile'); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + + // Stub all directories + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const directory = strip.getDirectory(sourceDirPath); + + strip.processNextFile(sourceDirPath, directory, {}); + + const testCompile = (callNumber: number, ...paths: string[]) => { + expect(compileStub.getCall(callNumber).args).to.deep.equal([ + path.resolve(sourceDirPath, ...paths), + {}, + ]); + }; + + expect(compileStub.callCount).to.equal(9); + + testCompile(0, 'test1.ts'); + testCompile(1, 'test2.ts'); + testCompile(2, 'testdir1', 'test11.ts'); + testCompile(3, 'testdir1', 'test12.tsx'); + testCompile(4, 'testdir1', 'test13.js'); + testCompile(5, 'test3.ts'); + testCompile(6, 'testdir2', 'test21.ts'); + testCompile(7, 'testdir2', 'test22.tsx'); + testCompile(8, 'testdir2', 'test23.js'); + + compileStub.restore(); + readDirStub.restore(); + statStub.restore(); + }); + + it('process files and directories using custom excludeDirPattern', () => { + const sourceDirPath = path.resolve(__dirname, 'src/testDir'); + const compileStub = sinon.stub(strip, 'compile'); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const directory = strip.getDirectory(sourceDirPath); + + const settings = { + excludeDirPattern: /(^|\\)(testdir1|node_modules|dist|\.next|out|\.generated)(\\|$)/gi, + }; + + strip.processNextFile(sourceDirPath, directory, settings); + + const testCompile = (callNumber: number, ...paths: string[]) => { + expect(compileStub.getCall(callNumber).args).to.deep.equal([ + path.resolve(sourceDirPath, ...paths), + settings, + ]); + }; + + expect(compileStub.callCount).to.equal(6); + + testCompile(0, 'test1.ts'); + testCompile(1, 'test2.ts'); + testCompile(2, 'test3.ts'); + testCompile(3, 'testdir2', 'test21.ts'); + testCompile(4, 'testdir2', 'test22.tsx'); + testCompile(5, 'testdir2', 'test23.js'); + + compileStub.restore(); + readDirStub.restore(); + statStub.restore(); + }); + + it('process files and directories using custom includeFilePattern', () => { + const sourceDirPath = path.resolve(__dirname, 'src/testDir'); + const compileStub = sinon.stub(strip, 'compile'); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + 'test99.js', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const directory = strip.getDirectory(sourceDirPath); + const settings = { includeFilePattern: /\.(js)$/ }; + + strip.processNextFile(sourceDirPath, directory, settings); + + const testCompile = (callNumber: number, ...paths: string[]) => { + expect(compileStub.getCall(callNumber).args).to.deep.equal([ + path.resolve(sourceDirPath, ...paths), + settings, + ]); + }; + + expect(compileStub.callCount).to.equal(3); + + testCompile(0, 'testdir1', 'test13.js'); + testCompile(1, 'testdir2', 'test23.js'); + testCompile(2, 'test99.js'); + + compileStub.restore(); + readDirStub.restore(); + statStub.restore(); + }); + }); + + describe('getDirectory', () => { + it('should iterate files', () => { + const sourceDirPath = path.resolve(__dirname, 'src/testDir'); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + + const directory = strip.getDirectory(sourceDirPath); + + expect(directory.index).to.equal(0); + expect(directory.files).to.deep.equal([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ]); + + expect(directory.getNextFile()).to.equal('node_modules'); + expect(directory.index).to.equal(1); + + expect(directory.getNextFile()).to.equal('test1.ts'); + expect(directory.index).to.equal(2); + + expect(directory.getNextFile()).to.equal('.next'); + expect(directory.index).to.equal(3); + + readDirStub.restore(); + }); + }); + + describe('strip', () => { + it('should process files and strip code', () => { + const sourceDirPath = process.cwd(); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon.stub(fs, 'readFileSync'); + + const stubFileContent = (filePath: string, content: string) => { + readFileSyncStub.withArgs(path.resolve(sourceDirPath, filePath)).returns(content); + }; + + stubFileContent( + 'test1.ts', + 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + ); + stubFileContent( + 'test2.ts', + 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + ); + stubFileContent( + 'testdir1/test11.ts', + 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + ); + stubFileContent( + 'testdir1/test12.tsx', + 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + ); + stubFileContent( + 'testdir1/test13.js', + 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + ); + stubFileContent( + 'test3.ts', + 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + ); + stubFileContent( + 'testdir2/test21.ts', + 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + ); + stubFileContent( + 'testdir2/test22.tsx', + 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + ); + stubFileContent( + 'testdir2/test23.js', + 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + ); + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const settings = { stripCode: true }; + + strip.strip(settings); + + expect(writeFileSyncStub.callCount).to.equal(9); + + const testCompile = (callIndex: number, filePath: string, content: string) => { + expect(writeFileSyncStub.getCall(callIndex).args).to.deep.equal([ + path.resolve(process.cwd(), filePath), + content, + ]); + }; + + testCompile(0, 'test1.ts', 'Test01\r\nTest04\r\nTest05'); + testCompile(1, 'test2.ts', 'Test11\r\nTest14\r\nTest15'); + testCompile(2, 'testdir1/test11.ts', 'Test21\r\nTest24\r\nTest25'); + testCompile(3, 'testdir1/test12.tsx', 'Test31\r\nTest34\r\nTest35'); + testCompile(4, 'testdir1/test13.js', 'Test41\r\nTest44\r\nTest45'); + testCompile(5, 'test3.ts', 'Test51\r\nTest54\r\nTest55'); + testCompile(6, 'testdir2/test21.ts', 'Test61\r\nTest64\r\nTest65'); + testCompile(7, 'testdir2/test22.tsx', 'Test71\r\nTest74\r\nTest75'); + testCompile(8, 'testdir2/test23.js', 'Test81\r\nTest84\r\nTest85'); + + readDirStub.restore(); + statStub.restore(); + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + + it('should process files and strip comments', () => { + const sourceDirPath = process.cwd(); + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon.stub(fs, 'readFileSync'); + + const stubFileContent = (filePath: string, content: string) => { + readFileSyncStub.withArgs(path.resolve(sourceDirPath, filePath)).returns(content); + }; + + stubFileContent( + 'test1.ts', + 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + ); + stubFileContent( + 'test2.ts', + 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + ); + stubFileContent( + 'testdir1/test11.ts', + 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + ); + stubFileContent( + 'testdir1/test12.tsx', + 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + ); + stubFileContent( + 'testdir1/test13.js', + 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + ); + stubFileContent( + 'test3.ts', + 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + ); + stubFileContent( + 'testdir2/test21.ts', + 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + ); + stubFileContent( + 'testdir2/test22.tsx', + 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + ); + stubFileContent( + 'testdir2/test23.js', + 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + ); + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const settings = { stripCode: false }; + + strip.strip(settings); + + expect(writeFileSyncStub.callCount).to.equal(9); + + const testCompile = (callIndex: number, filePath: string, content: string) => { + expect(writeFileSyncStub.getCall(callIndex).args).to.deep.equal([ + path.resolve(process.cwd(), filePath), + content, + ]); + }; + + testCompile(0, 'test1.ts', 'Test01\r\nTest02 Test03\r\nTest04\r\nTest05'); + testCompile(1, 'test2.ts', 'Test11\r\nTest12 Test13\r\nTest14\r\nTest15'); + testCompile(2, 'testdir1/test11.ts', 'Test21\r\nTest22 Test23\r\nTest24\r\nTest25'); + testCompile(3, 'testdir1/test12.tsx', 'Test31\r\nTest32 Test33\r\nTest34\r\nTest35'); + testCompile(4, 'testdir1/test13.js', 'Test41\r\nTest42 Test43\r\nTest44\r\nTest45'); + testCompile(5, 'test3.ts', 'Test51\r\nTest52 Test53\r\nTest54\r\nTest55'); + testCompile(6, 'testdir2/test21.ts', 'Test61\r\nTest62 Test63\r\nTest64\r\nTest65'); + testCompile(7, 'testdir2/test22.tsx', 'Test71\r\nTest72 Test73\r\nTest74\r\nTest75'); + testCompile(8, 'testdir2/test23.js', 'Test81\r\nTest82 Test83\r\nTest84\r\nTest85'); + + readDirStub.restore(); + statStub.restore(); + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + + it('should process files and strip code using custom sourcePath', () => { + const sourceDirPath = path.resolve(process.cwd(), 'sub_dir'); + + const readDirStub = sinon.stub(fs, 'readdirSync') as any; + const statStub = sinon.stub(fs, 'statSync'); + const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); + const readFileSyncStub = sinon.stub(fs, 'readFileSync'); + + const stubFileContent = (filePath: string, content: string) => { + readFileSyncStub.withArgs(path.resolve(sourceDirPath, filePath)).returns(content); + }; + + stubFileContent( + 'test1.ts', + 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + ); + stubFileContent( + 'test2.ts', + 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + ); + stubFileContent( + 'testdir1/test11.ts', + 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + ); + stubFileContent( + 'testdir1/test12.tsx', + 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + ); + stubFileContent( + 'testdir1/test13.js', + 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + ); + stubFileContent( + 'test3.ts', + 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + ); + stubFileContent( + 'testdir2/test21.ts', + 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + ); + stubFileContent( + 'testdir2/test22.tsx', + 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + ); + stubFileContent( + 'testdir2/test23.js', + 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + ); + + readDirStub + .withArgs(sourceDirPath) + .returns(([ + 'node_modules', + 'test1.ts', + '.next', + 'test2.ts', + 'testdir1', + 'test3.ts', + 'testdir2', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'node_modules')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, '.next')) + .returns(([ + 'script1.ts', + 'script2.tsx', + 'script3.js', + 'excludedFile.json', + ] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir1')) + .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); + readDirStub + .withArgs(path.resolve(sourceDirPath, 'testdir2')) + .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); + + statStub.returns({ isDirectory: () => false } as Stats); + + const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; + + dirPaths.forEach((dir) => { + statStub + .withArgs(path.resolve(sourceDirPath, dir)) + .returns({ isDirectory: () => true } as Stats); + }); + + const settings = { stripCode: true, sourcePath: sourceDirPath }; + + strip.strip(settings); + + expect(writeFileSyncStub.callCount).to.equal(9); + + // Checks that files were written + const testCompile = (callIndex: number, filePath: string, content: string) => { + expect(writeFileSyncStub.getCall(callIndex).args).to.deep.equal([ + path.resolve(process.cwd(), filePath), + content, + ]); + }; + + testCompile(0, 'sub_dir/test1.ts', 'Test01\r\nTest04\r\nTest05'); + testCompile(1, 'sub_dir/test2.ts', 'Test11\r\nTest14\r\nTest15'); + testCompile(2, 'sub_dir/testdir1/test11.ts', 'Test21\r\nTest24\r\nTest25'); + testCompile(3, 'sub_dir/testdir1/test12.tsx', 'Test31\r\nTest34\r\nTest35'); + testCompile(4, 'sub_dir/testdir1/test13.js', 'Test41\r\nTest44\r\nTest45'); + testCompile(5, 'sub_dir/test3.ts', 'Test51\r\nTest54\r\nTest55'); + testCompile(6, 'sub_dir/testdir2/test21.ts', 'Test61\r\nTest64\r\nTest65'); + testCompile(7, 'sub_dir/testdir2/test22.tsx', 'Test71\r\nTest74\r\nTest75'); + testCompile(8, 'sub_dir/testdir2/test23.js', 'Test81\r\nTest84\r\nTest85'); + + readDirStub.restore(); + statStub.restore(); + writeFileSyncStub.restore(); + readFileSyncStub.restore(); + }); + }); +}); diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.ts new file mode 100644 index 0000000000..56b1a0bc7e --- /dev/null +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.ts @@ -0,0 +1,168 @@ +import path from 'path'; +import fs from 'fs'; + +const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|dist|\.next|out|\.generated)(\\|$)/gi; +const INCLUDE_FILE_REGEXP = /\.(js|tsx?)$/i; + +/** + * Generates comments block + * @param {string} prefix starting part + * @param {string} [suffix] ending part + * @returns {RegExp} regExp + */ +const getStripRegExp = (prefix: string, suffix = 'EMPTY') => { + return new RegExp(`// #${prefix}_${suffix}`, 'g'); +}; + +interface StripSettings { + /** + * Strip function starting path + * @default process.cwd() + */ + sourcePath?: string; + /** + * Custom identificator for comments block in case if you want to have comments with special name + * @default 'EMPTY' + */ + suffix?: string; + /** + * Should function strip code and comments. By default it will strip only comments. + * @default false + */ + stripCode?: boolean; + /** + * Pattern to exclude specific directories from iterations + * @default /(^|\\)(node_modules|dist|\.next|out|\.generated)(\\|$)/gi + */ + excludeDirPattern?: RegExp; + /** + * Pattern to exclude files with specific filename from iterations + * @default /\.(js|tsx?)$/i + */ + includeFilePattern?: RegExp; +} + +/** + * Remove part of code which inside the comments block + * @param {string} file + * @param {StripSettings} settings + */ +export const compile = (file: string, settings: StripSettings) => { + const content = fs.readFileSync(file, 'utf8'); + + let shouldRemove = false; + + const lines = content.split('\r\n').filter((line) => { + const isStartLine = getStripRegExp('START', settings.suffix).test(line); + const isEndLine = getStripRegExp('END', settings.suffix).test(line); + + if (!settings.stripCode) { + return !(isStartLine || isEndLine); + } + + if (isStartLine) { + shouldRemove = true; + } + + if (isEndLine) { + shouldRemove = false; + + return shouldRemove; + } + + return !shouldRemove; + }); + + fs.writeFileSync(file, lines.join('\r\n')); +}; + +/** + * Process next file from the specified directory + * @param {string} dirPath + * @param {Directory} directory + * @param {StripSettings} settings + */ +export const processNextFile = ( + dirPath: string, + directory: Directory, + settings: StripSettings +): void => { + let file = directory.getNextFile(); + + if (!file) return; + + if ((settings.excludeDirPattern || EXCLUDE_DIR_REGEXP).test(file)) { + return processNextFile(dirPath, directory, settings); + } + + file = path.resolve(dirPath, file); + + const stat = fs.statSync(file); + + if (stat && stat.isDirectory()) { + processDirectory(file, settings); + processNextFile(dirPath, directory, settings); + + return; + } + + if (!(settings.includeFilePattern || INCLUDE_FILE_REGEXP).test(file)) { + return processNextFile(dirPath, directory, settings); + } + + compile(file, settings); + + processNextFile(dirPath, directory, settings); +}; + +interface Directory { + /** + * Files list in the directory + */ + files: string[]; + /** + * Current file index + */ + index: number; + /** + * Returns next file in the directory + */ + getNextFile(): string; +} + +/** + * Get files list and returns @interface Directory instance + * @param {string} dir directory path + * @returns {Directory} instance + */ +export const getDirectory = (dir: string): Directory => { + const list = fs.readdirSync(dir); + + return { + files: list, + index: 0, + getNextFile() { + return this.files[this.index++]; + }, + }; +}; + +/** + * Iterate files/directories in provided directory + * @param {string} dirPath current directory + * @param {StripSettings} settings + */ +export const processDirectory = (dirPath: string, settings: StripSettings) => { + const directory = getDirectory(dirPath); + + processNextFile(dirPath, directory, settings); +}; + +/** + * Removes part of code which inside the special comments block. + * Compiles each not excluded file starting from current dirrectory (or `settings.sourcePath`). + * @param {StripSettings} settings + */ +export const strip = (settings: StripSettings = {}) => { + processDirectory(settings.sourcePath || process.cwd(), settings); +}; diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index 7cfe1c7d89..ae794fc887 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -1,9 +1,9 @@ const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); +const { strip } = require('@sitecore-jss/sitecore-jss-dev-tools'); const { applyNameToProject } = require('@sitecore-jss/sitecore-jss-cli/dist/create'); const { execSync } = require('child_process'); -const strip = require('./scripts/strip'); /** * This function is invoked by `jss create` when an app based on this template is created. @@ -43,6 +43,8 @@ module.exports = function createJssProject(argv, nextSteps) { createEmptyStarter(); } + strip({ stripCode: argv.empty }); + setFetchWith(argv.fetchWith); setPrerender(argv.prerender); setNextConfig(); @@ -59,7 +61,6 @@ function createEmptyStarter() { const dataDir = getPath('data'); const disconnectedProxyScript = getPath('scripts/disconnected-mode-proxy.ts'); const manifestTemplate = getPath('scripts/templates/component-manifest.ts'); - const stripScript = getPath('scripts/strip.js'); const definitionsDir = getPath('sitecore/definitions'); const componentsDir = getPath('src/components'); @@ -70,11 +71,7 @@ function createEmptyStarter() { fs.unlinkSync(manifestTemplate); fs.rmdirSync(definitionsDir, { recursive: true }); fs.rmdirSync(componentsDir, { recursive: true }); - - strip(); - fs.mkdirSync(componentsDir); - fs.unlinkSync(stripScript); const packageJson = require('./package.json'); diff --git a/samples/nextjs/scripts/strip.js b/samples/nextjs/scripts/strip.js deleted file mode 100644 index 72adef7a15..0000000000 --- a/samples/nextjs/scripts/strip.js +++ /dev/null @@ -1,89 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|\.next|out|\.generated)(\\|$)/gi; -const INCLUDE_FILE_REGEXP = /\.(js|tsx?)$/g; - -const START_REGEXP = /\/\/ #START_STRIP/g; -const END_REGEXP = /\/\/ #END_STRIP/g; - -/** - * Remove part of code which inside the STRIP block - * @param {string} file - */ -const compile = (file) => { - const content = fs.readFileSync(file, 'utf8'); - - let shouldRemove = false; - - const lines = content.split('\r\n').filter((line) => { - if (START_REGEXP.test(line)) { - shouldRemove = true; - } - - if (END_REGEXP.test(line)) { - shouldRemove = false; - - return shouldRemove; - } - - return !shouldRemove; - }); - - fs.writeFileSync(file, lines.join('\r\n')); -}; - -/** - * Iterate files/directories in provided directory - * @param {string} dir current directory - * @param {Function} done called when all files are iterated in directory - */ -const iterate = function (dir, done) { - let results = []; - - try { - const list = fs.readdirSync(dir); - - let i = 0; - - const nextFile = () => { - let file = list[i++]; - - if (!file) return done(null); - - if (EXCLUDE_DIR_REGEXP.test(file)) { - return nextFile(); - } - - file = path.resolve(dir, file); - - const stat = fs.statSync(file); - - if (stat && stat.isDirectory()) { - iterate(file, () => { - nextFile(); - }); - - return; - } - - if (!INCLUDE_FILE_REGEXP.test(file)) return nextFile(); - - compile(file); - - results.push(file); - - nextFile(); - }; - - nextFile(); - } catch (error) { - done(error); - } -}; - -module.exports = () => { - iterate(path.join(__dirname, '../'), (err) => { - if (err) throw err; - }); -}; From 23a8d0b444e75ae0c0c4b427de43e9ccfb6bc3d3 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 4 Aug 2021 15:36:40 +0300 Subject: [PATCH 16/20] change doc --- docs/data/routes/docs/fundamentals/cli/en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/routes/docs/fundamentals/cli/en.md b/docs/data/routes/docs/fundamentals/cli/en.md index 8ec675abe5..92908840da 100644 --- a/docs/data/routes/docs/fundamentals/cli/en.md +++ b/docs/data/routes/docs/fundamentals/cli/en.md @@ -39,7 +39,7 @@ jss create |`--proxy`, `-p` | Specifies a HTTP proxy when downloading templates. | A local directory path | - | all | |`--fetchWith` | Specifies how the applicaiton should fetch Sitecore layout and dictionary data. |`REST` or `GraphQL` | `REST` | nextjs | |`--prerender` | Specifies the Next.js pre-rendering form for the primary `[[...path]].tsx` route. | `SSG` or `SSR` | `SSG` | nextjs | -|`--empty` | Specifies the app should be created empty | - | `false` | all | +|`--empty` | Specifies the app should be created empty | - | `false` | nextjs | **Examples** ``` From 598069a7cb6b53b0d793ec0f5867d94f85b51ec8 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Thu, 5 Aug 2021 12:27:33 +0300 Subject: [PATCH 17/20] Refactoring --- .../src/templating/strip.test.ts | 538 +++--------------- .../src/templating/strip.ts | 119 +--- samples/nextjs/jss-create.js | 17 +- 3 files changed, 106 insertions(+), 568 deletions(-) diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts index 6aa4597ac5..37a6d4cf01 100644 --- a/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts @@ -2,7 +2,8 @@ import { expect } from 'chai'; import sinon from 'sinon'; import path from 'path'; -import fs, { Dirent, Stats } from 'fs'; +import fs from 'fs'; +import glob from 'glob'; import * as strip from './strip'; describe('strip', () => { @@ -12,7 +13,7 @@ describe('strip', () => { const readFileSyncStub = sinon .stub(fs, 'readFileSync') .returns( - 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\n// #START_JSS\r\nTest99\r\n// #END_JSS' + 'Test11\r\n// #START_STRIP\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_STRIP\r\nTest2\r\n// #START_JSS\r\nTest99\r\n// #END_JSS' ); strip.compile('test.ts', { stripCode: false }); @@ -32,7 +33,7 @@ describe('strip', () => { const readFileSyncStub = sinon .stub(fs, 'readFileSync') .returns( - 'Test55\r\n// #START_EMPTY\r\n// #START_JSS\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_JSS\r\n// #END_EMPTY' + 'Test55\r\n// #START_STRIP\r\n// #START_JSS\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_JSS\r\n// #END_STRIP' ); strip.compile('test.ts', { stripCode: false, suffix: 'JSS' }); @@ -40,7 +41,7 @@ describe('strip', () => { expect(writeFileSyncStub.calledOnce).to.be.true; expect(writeFileSyncStub.args[0]).to.deep.equal([ 'test.ts', - 'Test55\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_EMPTY', + 'Test55\r\n// #START_STRIP\r\nTest12 Test13\r\nTest14 Test15\r\nTest99\r\n// #END_STRIP', ]); writeFileSyncStub.restore(); @@ -52,7 +53,7 @@ describe('strip', () => { const readFileSyncStub = sinon .stub(fs, 'readFileSync') .returns( - 'Test11\r\n// #START_JSS\r\n// #START_EMPTY\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34\r\n// #END_JSS' + 'Test11\r\n// #START_JSS\r\n// #START_STRIP\r\nTest12 Test13\r\nTest14 Test15\r\n// #END_STRIP\r\nTest2\r\nTest34\r\n// #END_JSS' ); strip.compile('test.ts', { stripCode: true }); @@ -72,7 +73,7 @@ describe('strip', () => { const readFileSyncStub = sinon .stub(fs, 'readFileSync') .returns( - 'Test11\r\n// #START_EMPTY\r\n// #START_JSS\r\nTest12 Test13\r\n// #END_JSS\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34' + 'Test11\r\n// #START_STRIP\r\n// #START_JSS\r\nTest12 Test13\r\n// #END_JSS\r\nTest14 Test15\r\n// #END_STRIP\r\nTest2\r\nTest34' ); strip.compile('test.ts', { stripCode: true, suffix: 'JSS' }); @@ -80,7 +81,7 @@ describe('strip', () => { expect(writeFileSyncStub.calledOnce).to.be.true; expect(writeFileSyncStub.args[0]).to.deep.equal([ 'test.ts', - 'Test11\r\n// #START_EMPTY\r\nTest14 Test15\r\n// #END_EMPTY\r\nTest2\r\nTest34', + 'Test11\r\n// #START_STRIP\r\nTest14 Test15\r\n// #END_STRIP\r\nTest2\r\nTest34', ]); writeFileSyncStub.restore(); @@ -88,290 +89,11 @@ describe('strip', () => { }); }); - describe('processNextFile', () => { - it('process files and directories', () => { - const sourceDirPath = path.resolve(__dirname, 'src/testDir'); - const compileStub = sinon.stub(strip, 'compile'); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); - - // Stub all directories - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); - - const directory = strip.getDirectory(sourceDirPath); - - strip.processNextFile(sourceDirPath, directory, {}); - - const testCompile = (callNumber: number, ...paths: string[]) => { - expect(compileStub.getCall(callNumber).args).to.deep.equal([ - path.resolve(sourceDirPath, ...paths), - {}, - ]); - }; - - expect(compileStub.callCount).to.equal(9); - - testCompile(0, 'test1.ts'); - testCompile(1, 'test2.ts'); - testCompile(2, 'testdir1', 'test11.ts'); - testCompile(3, 'testdir1', 'test12.tsx'); - testCompile(4, 'testdir1', 'test13.js'); - testCompile(5, 'test3.ts'); - testCompile(6, 'testdir2', 'test21.ts'); - testCompile(7, 'testdir2', 'test22.tsx'); - testCompile(8, 'testdir2', 'test23.js'); - - compileStub.restore(); - readDirStub.restore(); - statStub.restore(); - }); - - it('process files and directories using custom excludeDirPattern', () => { - const sourceDirPath = path.resolve(__dirname, 'src/testDir'); - const compileStub = sinon.stub(strip, 'compile'); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); - - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); - - const directory = strip.getDirectory(sourceDirPath); - - const settings = { - excludeDirPattern: /(^|\\)(testdir1|node_modules|dist|\.next|out|\.generated)(\\|$)/gi, - }; - - strip.processNextFile(sourceDirPath, directory, settings); - - const testCompile = (callNumber: number, ...paths: string[]) => { - expect(compileStub.getCall(callNumber).args).to.deep.equal([ - path.resolve(sourceDirPath, ...paths), - settings, - ]); - }; - - expect(compileStub.callCount).to.equal(6); - - testCompile(0, 'test1.ts'); - testCompile(1, 'test2.ts'); - testCompile(2, 'test3.ts'); - testCompile(3, 'testdir2', 'test21.ts'); - testCompile(4, 'testdir2', 'test22.tsx'); - testCompile(5, 'testdir2', 'test23.js'); - - compileStub.restore(); - readDirStub.restore(); - statStub.restore(); - }); - - it('process files and directories using custom includeFilePattern', () => { - const sourceDirPath = path.resolve(__dirname, 'src/testDir'); - const compileStub = sinon.stub(strip, 'compile'); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); - - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - 'test99.js', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); - - const directory = strip.getDirectory(sourceDirPath); - const settings = { includeFilePattern: /\.(js)$/ }; - - strip.processNextFile(sourceDirPath, directory, settings); - - const testCompile = (callNumber: number, ...paths: string[]) => { - expect(compileStub.getCall(callNumber).args).to.deep.equal([ - path.resolve(sourceDirPath, ...paths), - settings, - ]); - }; - - expect(compileStub.callCount).to.equal(3); - - testCompile(0, 'testdir1', 'test13.js'); - testCompile(1, 'testdir2', 'test23.js'); - testCompile(2, 'test99.js'); - - compileStub.restore(); - readDirStub.restore(); - statStub.restore(); - }); - }); - - describe('getDirectory', () => { - it('should iterate files', () => { - const sourceDirPath = path.resolve(__dirname, 'src/testDir'); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - - const directory = strip.getDirectory(sourceDirPath); - - expect(directory.index).to.equal(0); - expect(directory.files).to.deep.equal([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ]); - - expect(directory.getNextFile()).to.equal('node_modules'); - expect(directory.index).to.equal(1); - - expect(directory.getNextFile()).to.equal('test1.ts'); - expect(directory.index).to.equal(2); - - expect(directory.getNextFile()).to.equal('.next'); - expect(directory.index).to.equal(3); - - readDirStub.restore(); - }); - }); - describe('strip', () => { it('should process files and strip code', () => { const sourceDirPath = process.cwd(); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); + const globStub = sinon.stub(glob, 'sync'); const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); const readFileSyncStub = sinon.stub(fs, 'readFileSync'); @@ -381,86 +103,52 @@ describe('strip', () => { stubFileContent( 'test1.ts', - 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + 'Test01\r\n// #START_STRIP\r\nTest02 Test03\r\n// #END_STRIP\r\nTest04\r\nTest05' ); stubFileContent( 'test2.ts', - 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + 'Test11\r\n// #START_STRIP\r\nTest12 Test13\r\n// #END_STRIP\r\nTest14\r\nTest15' ); stubFileContent( 'testdir1/test11.ts', - 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + 'Test21\r\n// #START_STRIP\r\nTest22 Test23\r\n// #END_STRIP\r\nTest24\r\nTest25' ); stubFileContent( 'testdir1/test12.tsx', - 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + 'Test31\r\n// #START_STRIP\r\nTest32 Test33\r\n// #END_STRIP\r\nTest34\r\nTest35' ); stubFileContent( 'testdir1/test13.js', - 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + 'Test41\r\n// #START_STRIP\r\nTest42 Test43\r\n// #END_STRIP\r\nTest44\r\nTest45' ); stubFileContent( 'test3.ts', - 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + 'Test51\r\n// #START_STRIP\r\nTest52 Test53\r\n// #END_STRIP\r\nTest54\r\nTest55' ); stubFileContent( 'testdir2/test21.ts', - 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + 'Test61\r\n// #START_STRIP\r\nTest62 Test63\r\n// #END_STRIP\r\nTest64\r\nTest65' ); stubFileContent( 'testdir2/test22.tsx', - 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + 'Test71\r\n// #START_STRIP\r\nTest72 Test73\r\n// #END_STRIP\r\nTest74\r\nTest75' ); stubFileContent( 'testdir2/test23.js', - 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + 'Test81\r\n// #START_STRIP\r\nTest82 Test83\r\n// #END_STRIP\r\nTest84\r\nTest85' ); - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); + globStub.returns([ + 'test1.ts', + 'test2.ts', + 'testdir1/test11.ts', + 'testdir1/test12.tsx', + 'testdir1/test13.js', + 'test3.ts', + 'testdir2/test21.ts', + 'testdir2/test22.tsx', + 'testdir2/test23.js', + ]); const settings = { stripCode: true }; @@ -485,17 +173,15 @@ describe('strip', () => { testCompile(7, 'testdir2/test22.tsx', 'Test71\r\nTest74\r\nTest75'); testCompile(8, 'testdir2/test23.js', 'Test81\r\nTest84\r\nTest85'); - readDirStub.restore(); - statStub.restore(); + globStub.restore(); writeFileSyncStub.restore(); readFileSyncStub.restore(); }); it('should process files and strip comments', () => { const sourceDirPath = process.cwd(); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); + const globStub = sinon.stub(glob, 'sync'); const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); const readFileSyncStub = sinon.stub(fs, 'readFileSync'); @@ -505,89 +191,54 @@ describe('strip', () => { stubFileContent( 'test1.ts', - 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + 'Test01\r\n// #START_STRIP\r\nTest02 Test03\r\n// #END_STRIP\r\nTest04\r\nTest05' ); stubFileContent( 'test2.ts', - 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + 'Test11\r\n// #START_STRIP\r\nTest12 Test13\r\n// #END_STRIP\r\nTest14\r\nTest15' ); stubFileContent( 'testdir1/test11.ts', - 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + 'Test21\r\n// #START_STRIP\r\nTest22 Test23\r\n// #END_STRIP\r\nTest24\r\nTest25' ); stubFileContent( 'testdir1/test12.tsx', - 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + 'Test31\r\n// #START_STRIP\r\nTest32 Test33\r\n// #END_STRIP\r\nTest34\r\nTest35' ); stubFileContent( 'testdir1/test13.js', - 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + 'Test41\r\n// #START_STRIP\r\nTest42 Test43\r\n// #END_STRIP\r\nTest44\r\nTest45' ); stubFileContent( 'test3.ts', - 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + 'Test51\r\n// #START_STRIP\r\nTest52 Test53\r\n// #END_STRIP\r\nTest54\r\nTest55' ); stubFileContent( 'testdir2/test21.ts', - 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + 'Test61\r\n// #START_STRIP\r\nTest62 Test63\r\n// #END_STRIP\r\nTest64\r\nTest65' ); stubFileContent( 'testdir2/test22.tsx', - 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + 'Test71\r\n// #START_STRIP\r\nTest72 Test73\r\n// #END_STRIP\r\nTest74\r\nTest75' ); stubFileContent( 'testdir2/test23.js', - 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + 'Test81\r\n// #START_STRIP\r\nTest82 Test83\r\n// #END_STRIP\r\nTest84\r\nTest85' ); - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); - - const settings = { stripCode: false }; + globStub.returns([ + 'test1.ts', + 'test2.ts', + 'testdir1/test11.ts', + 'testdir1/test12.tsx', + 'testdir1/test13.js', + 'test3.ts', + 'testdir2/test21.ts', + 'testdir2/test22.tsx', + 'testdir2/test23.js', + ]); - strip.strip(settings); + strip.strip(); expect(writeFileSyncStub.callCount).to.equal(9); @@ -608,8 +259,7 @@ describe('strip', () => { testCompile(7, 'testdir2/test22.tsx', 'Test71\r\nTest72 Test73\r\nTest74\r\nTest75'); testCompile(8, 'testdir2/test23.js', 'Test81\r\nTest82 Test83\r\nTest84\r\nTest85'); - readDirStub.restore(); - statStub.restore(); + globStub.restore(); writeFileSyncStub.restore(); readFileSyncStub.restore(); }); @@ -617,8 +267,7 @@ describe('strip', () => { it('should process files and strip code using custom sourcePath', () => { const sourceDirPath = path.resolve(process.cwd(), 'sub_dir'); - const readDirStub = sinon.stub(fs, 'readdirSync') as any; - const statStub = sinon.stub(fs, 'statSync'); + const globStub = sinon.stub(glob, 'sync'); const writeFileSyncStub = sinon.stub(fs, 'writeFileSync'); const readFileSyncStub = sinon.stub(fs, 'readFileSync'); @@ -628,86 +277,54 @@ describe('strip', () => { stubFileContent( 'test1.ts', - 'Test01\r\n// #START_EMPTY\r\nTest02 Test03\r\n// #END_EMPTY\r\nTest04\r\nTest05' + 'Test01\r\n// #START_STRIP\r\nTest02 Test03\r\n// #END_STRIP\r\nTest04\r\nTest05' ); stubFileContent( 'test2.ts', - 'Test11\r\n// #START_EMPTY\r\nTest12 Test13\r\n// #END_EMPTY\r\nTest14\r\nTest15' + 'Test11\r\n// #START_STRIP\r\nTest12 Test13\r\n// #END_STRIP\r\nTest14\r\nTest15' ); stubFileContent( 'testdir1/test11.ts', - 'Test21\r\n// #START_EMPTY\r\nTest22 Test23\r\n// #END_EMPTY\r\nTest24\r\nTest25' + 'Test21\r\n// #START_STRIP\r\nTest22 Test23\r\n// #END_STRIP\r\nTest24\r\nTest25' ); stubFileContent( 'testdir1/test12.tsx', - 'Test31\r\n// #START_EMPTY\r\nTest32 Test33\r\n// #END_EMPTY\r\nTest34\r\nTest35' + 'Test31\r\n// #START_STRIP\r\nTest32 Test33\r\n// #END_STRIP\r\nTest34\r\nTest35' ); stubFileContent( 'testdir1/test13.js', - 'Test41\r\n// #START_EMPTY\r\nTest42 Test43\r\n// #END_EMPTY\r\nTest44\r\nTest45' + 'Test41\r\n// #START_STRIP\r\nTest42 Test43\r\n// #END_STRIP\r\nTest44\r\nTest45' ); stubFileContent( 'test3.ts', - 'Test51\r\n// #START_EMPTY\r\nTest52 Test53\r\n// #END_EMPTY\r\nTest54\r\nTest55' + 'Test51\r\n// #START_STRIP\r\nTest52 Test53\r\n// #END_STRIP\r\nTest54\r\nTest55' ); stubFileContent( 'testdir2/test21.ts', - 'Test61\r\n// #START_EMPTY\r\nTest62 Test63\r\n// #END_EMPTY\r\nTest64\r\nTest65' + 'Test61\r\n// #START_STRIP\r\nTest62 Test63\r\n// #END_STRIP\r\nTest64\r\nTest65' ); stubFileContent( 'testdir2/test22.tsx', - 'Test71\r\n// #START_EMPTY\r\nTest72 Test73\r\n// #END_EMPTY\r\nTest74\r\nTest75' + 'Test71\r\n// #START_STRIP\r\nTest72 Test73\r\n// #END_STRIP\r\nTest74\r\nTest75' ); stubFileContent( 'testdir2/test23.js', - 'Test81\r\n// #START_EMPTY\r\nTest82 Test83\r\n// #END_EMPTY\r\nTest84\r\nTest85' + 'Test81\r\n// #START_STRIP\r\nTest82 Test83\r\n// #END_STRIP\r\nTest84\r\nTest85' ); - readDirStub - .withArgs(sourceDirPath) - .returns(([ - 'node_modules', - 'test1.ts', - '.next', - 'test2.ts', - 'testdir1', - 'test3.ts', - 'testdir2', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'node_modules')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, '.next')) - .returns(([ - 'script1.ts', - 'script2.tsx', - 'script3.js', - 'excludedFile.json', - ] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir1')) - .returns((['test11.ts', 'test12.tsx', 'test13.js'] as unknown) as Dirent[]); - readDirStub - .withArgs(path.resolve(sourceDirPath, 'testdir2')) - .returns((['test21.ts', 'test22.tsx', 'test23.js'] as unknown) as Dirent[]); - - statStub.returns({ isDirectory: () => false } as Stats); - - const dirPaths = ['node_modules', '.next', 'testdir1', 'testdir2']; - - dirPaths.forEach((dir) => { - statStub - .withArgs(path.resolve(sourceDirPath, dir)) - .returns({ isDirectory: () => true } as Stats); - }); - - const settings = { stripCode: true, sourcePath: sourceDirPath }; + globStub.returns([ + 'test1.ts', + 'test2.ts', + 'testdir1/test11.ts', + 'testdir1/test12.tsx', + 'testdir1/test13.js', + 'test3.ts', + 'testdir2/test21.ts', + 'testdir2/test22.tsx', + 'testdir2/test23.js', + ]); + + const settings = { stripCode: true, cwd: sourceDirPath }; strip.strip(settings); @@ -731,8 +348,7 @@ describe('strip', () => { testCompile(7, 'sub_dir/testdir2/test22.tsx', 'Test71\r\nTest74\r\nTest75'); testCompile(8, 'sub_dir/testdir2/test23.js', 'Test81\r\nTest84\r\nTest85'); - readDirStub.restore(); - statStub.restore(); + globStub.restore(); writeFileSyncStub.restore(); readFileSyncStub.restore(); }); diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.ts index 56b1a0bc7e..db049bc4e8 100644 --- a/packages/sitecore-jss-dev-tools/src/templating/strip.ts +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.ts @@ -1,8 +1,9 @@ import path from 'path'; import fs from 'fs'; +import glob from 'glob'; -const EXCLUDE_DIR_REGEXP = /(^|\\)(node_modules|dist|\.next|out|\.generated)(\\|$)/gi; -const INCLUDE_FILE_REGEXP = /\.(js|tsx?)$/i; +const PATTERN = '**/*.@(js|ts?(x))'; +const IGNORE_PATTERN = '@(node_modules|dist|.next|out|.generated)/**'; /** * Generates comments block @@ -10,19 +11,14 @@ const INCLUDE_FILE_REGEXP = /\.(js|tsx?)$/i; * @param {string} [suffix] ending part * @returns {RegExp} regExp */ -const getStripRegExp = (prefix: string, suffix = 'EMPTY') => { +const getStripRegExp = (prefix: string, suffix = 'STRIP') => { return new RegExp(`// #${prefix}_${suffix}`, 'g'); }; interface StripSettings { - /** - * Strip function starting path - * @default process.cwd() - */ - sourcePath?: string; /** * Custom identificator for comments block in case if you want to have comments with special name - * @default 'EMPTY' + * @default 'STRIP' */ suffix?: string; /** @@ -31,15 +27,20 @@ interface StripSettings { */ stripCode?: boolean; /** - * Pattern to exclude specific directories from iterations - * @default /(^|\\)(node_modules|dist|\.next|out|\.generated)(\\|$)/gi + * Indicates which files should be included + * @default '!(node_modules|dist|.next|out|.generated)/**\/*.@(js|ts?(x))' */ - excludeDirPattern?: RegExp; + pattern?: string; /** - * Pattern to exclude files with specific filename from iterations - * @default /\.(js|tsx?)$/i + * Indicates which files should be ignored + * @default '@(node_modules|dist|.next|out|.generated)/**' + */ + ignore?: string; + /** + * Current working directory + * @default process.cwd() */ - includeFilePattern?: RegExp; + cwd?: string; } /** @@ -76,93 +77,15 @@ export const compile = (file: string, settings: StripSettings) => { fs.writeFileSync(file, lines.join('\r\n')); }; -/** - * Process next file from the specified directory - * @param {string} dirPath - * @param {Directory} directory - * @param {StripSettings} settings - */ -export const processNextFile = ( - dirPath: string, - directory: Directory, - settings: StripSettings -): void => { - let file = directory.getNextFile(); - - if (!file) return; - - if ((settings.excludeDirPattern || EXCLUDE_DIR_REGEXP).test(file)) { - return processNextFile(dirPath, directory, settings); - } - - file = path.resolve(dirPath, file); - - const stat = fs.statSync(file); - - if (stat && stat.isDirectory()) { - processDirectory(file, settings); - processNextFile(dirPath, directory, settings); - - return; - } - - if (!(settings.includeFilePattern || INCLUDE_FILE_REGEXP).test(file)) { - return processNextFile(dirPath, directory, settings); - } - - compile(file, settings); - - processNextFile(dirPath, directory, settings); -}; - -interface Directory { - /** - * Files list in the directory - */ - files: string[]; - /** - * Current file index - */ - index: number; - /** - * Returns next file in the directory - */ - getNextFile(): string; -} - -/** - * Get files list and returns @interface Directory instance - * @param {string} dir directory path - * @returns {Directory} instance - */ -export const getDirectory = (dir: string): Directory => { - const list = fs.readdirSync(dir); - - return { - files: list, - index: 0, - getNextFile() { - return this.files[this.index++]; - }, - }; -}; - -/** - * Iterate files/directories in provided directory - * @param {string} dirPath current directory - * @param {StripSettings} settings - */ -export const processDirectory = (dirPath: string, settings: StripSettings) => { - const directory = getDirectory(dirPath); - - processNextFile(dirPath, directory, settings); -}; - /** * Removes part of code which inside the special comments block. * Compiles each not excluded file starting from current dirrectory (or `settings.sourcePath`). * @param {StripSettings} settings */ export const strip = (settings: StripSettings = {}) => { - processDirectory(settings.sourcePath || process.cwd(), settings); + const { pattern = PATTERN, ignore = IGNORE_PATTERN, cwd = process.cwd() } = settings; + + const files = glob.sync(pattern, { ignore, cwd }); + + files.forEach((file) => compile(path.resolve(cwd, file), settings)); }; diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index ae794fc887..463f427ebc 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -39,12 +39,7 @@ module.exports = function createJssProject(argv, nextSteps) { ); } - if (argv.empty) { - createEmptyStarter(); - } - - strip({ stripCode: argv.empty }); - + setEmpty(argv.empty); setFetchWith(argv.fetchWith); setPrerender(argv.prerender); setNextConfig(); @@ -57,15 +52,19 @@ function getPath(filepath) { return path.join(__dirname, filepath); } -function createEmptyStarter() { +function setEmpty(empty) { + console.log(chalk.cyan('Cleaning up the sample...')); + + strip({ stripCode: empty, suffix: 'EMPTY' }); + + if (!empty) return; + const dataDir = getPath('data'); const disconnectedProxyScript = getPath('scripts/disconnected-mode-proxy.ts'); const manifestTemplate = getPath('scripts/templates/component-manifest.ts'); const definitionsDir = getPath('sitecore/definitions'); const componentsDir = getPath('src/components'); - console.log(chalk.cyan('Cleaning up the sample...')); - fs.rmdirSync(dataDir, { recursive: true }); fs.unlinkSync(disconnectedProxyScript); fs.unlinkSync(manifestTemplate); From 8190b36b2cebe5f673532133c13a0dcb632013f8 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Thu, 5 Aug 2021 13:16:00 +0300 Subject: [PATCH 18/20] update script --- samples/nextjs/jss-create.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/nextjs/jss-create.js b/samples/nextjs/jss-create.js index bb07aa526e..a87b4cf5ea 100644 --- a/samples/nextjs/jss-create.js +++ b/samples/nextjs/jss-create.js @@ -35,11 +35,11 @@ module.exports = function createJssProject(argv, nextSteps) { )} : Specifies the Next.js pre-rendering form for the optional catch-all route. Default is SSG.`, `* ${chalk.green( '--empty {true|false}' - )} : Specifies whether the sample should be empty. Disconnected mode and styleguide components will be removed. Default is false.`, + )} : Specifies whether the sample should be empty. Disconnected mode and styleguide components will be removed. Default is false.` ); } - setEmpty(argv.empty); + setEmpty(argv.empty); setFetchWith(argv.fetchWith); setPrerender(argv.prerender); setNextConfig(); @@ -55,7 +55,7 @@ function getPath(filepath) { function setEmpty(empty) { console.log(chalk.cyan('Cleaning up the sample...')); - strip({ stripCode: empty, suffix: 'EMPTY' }); + strip({ stripCode: empty, suffix: 'EMPTY', cwd: getPath('/') }); if (!empty) return; From d5b26d46802f55682c6bed0e7b08c549827ebd17 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Thu, 5 Aug 2021 13:19:31 +0300 Subject: [PATCH 19/20] rename test --- packages/sitecore-jss-dev-tools/src/templating/strip.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts index 37a6d4cf01..60730f8b51 100644 --- a/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.test.ts @@ -264,7 +264,7 @@ describe('strip', () => { readFileSyncStub.restore(); }); - it('should process files and strip code using custom sourcePath', () => { + it('should process files and strip code using custom cwd', () => { const sourceDirPath = path.resolve(process.cwd(), 'sub_dir'); const globStub = sinon.stub(glob, 'sync'); From 60f30343588f383176d1d8ec05a9253f21947266 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Fri, 6 Aug 2021 09:27:13 +0300 Subject: [PATCH 20/20] Fix tsdoc --- packages/sitecore-jss-dev-tools/src/templating/strip.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sitecore-jss-dev-tools/src/templating/strip.ts b/packages/sitecore-jss-dev-tools/src/templating/strip.ts index db049bc4e8..d429e36c8e 100644 --- a/packages/sitecore-jss-dev-tools/src/templating/strip.ts +++ b/packages/sitecore-jss-dev-tools/src/templating/strip.ts @@ -28,7 +28,7 @@ interface StripSettings { stripCode?: boolean; /** * Indicates which files should be included - * @default '!(node_modules|dist|.next|out|.generated)/**\/*.@(js|ts?(x))' + * @default '**\/*.@(js|ts?(x))' */ pattern?: string; /**