create-sitecore-jss 20.0.0-canary.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/LICENSE.MD +202 -0
  2. package/dist/InitializerFactory.js +49 -0
  3. package/dist/common/Initializer.js +2 -0
  4. package/dist/common/args/base.js +2 -0
  5. package/dist/common/args/styleguide.js +2 -0
  6. package/dist/common/prompts/base.js +40 -0
  7. package/dist/common/prompts/styleguide.js +28 -0
  8. package/dist/common/steps/index.js +13 -0
  9. package/dist/common/steps/install.js +46 -0
  10. package/dist/common/steps/next.js +60 -0
  11. package/dist/common/steps/transform.js +185 -0
  12. package/dist/common/utils/cmd.js +39 -0
  13. package/dist/common/utils/helpers.js +92 -0
  14. package/dist/index.js +91 -0
  15. package/dist/init-runner.js +50 -0
  16. package/dist/initializers/nextjs/args.js +2 -0
  17. package/dist/initializers/nextjs/index.js +65 -0
  18. package/dist/initializers/nextjs/prompts.js +19 -0
  19. package/dist/initializers/nextjs/remove-dev-dependencies.js +24 -0
  20. package/dist/initializers/nextjs-styleguide/index.js +49 -0
  21. package/dist/templates/nextjs/.env +49 -0
  22. package/dist/templates/nextjs/.eslintrc +25 -0
  23. package/dist/templates/nextjs/.gitattributes +11 -0
  24. package/dist/templates/nextjs/.gitignore +33 -0
  25. package/dist/templates/nextjs/.graphql-let.yml +10 -0
  26. package/dist/templates/nextjs/.prettierignore +1 -0
  27. package/dist/templates/nextjs/.prettierrc +8 -0
  28. package/dist/templates/nextjs/LICENSE.txt +202 -0
  29. package/dist/templates/nextjs/README.md +3 -0
  30. package/dist/templates/nextjs/next-env.d.ts +6 -0
  31. package/dist/templates/nextjs/next.config.js +56 -0
  32. package/dist/templates/nextjs/package.json +88 -0
  33. package/dist/templates/nextjs/public/favicon.ico +0 -0
  34. package/dist/templates/nextjs/public/sc_logo.svg +20 -0
  35. package/dist/templates/nextjs/scripts/bootstrap.ts +40 -0
  36. package/dist/templates/nextjs/scripts/fetch-graphql-introspection-data.ts +49 -0
  37. package/dist/templates/nextjs/scripts/generate-component-factory.ts +95 -0
  38. package/dist/templates/nextjs/scripts/generate-config.ts +85 -0
  39. package/dist/templates/nextjs/scripts/generate-plugins.ts +103 -0
  40. package/dist/templates/nextjs/scripts/scaffold-component.ts +89 -0
  41. package/dist/templates/nextjs/scripts/templates/component-factory.ts +119 -0
  42. package/dist/templates/nextjs/scripts/templates/component-src.ts +27 -0
  43. package/dist/templates/nextjs/scripts/utils.ts +59 -0
  44. package/dist/templates/nextjs/sitecore/config/{{appName}}.config +151 -0
  45. package/dist/templates/nextjs/src/Layout.tsx +53 -0
  46. package/dist/templates/nextjs/src/Navigation.tsx +27 -0
  47. package/dist/templates/nextjs/src/NotFound.tsx +19 -0
  48. package/dist/templates/nextjs/src/assets/app.css +28 -0
  49. package/dist/templates/nextjs/src/components/ContentBlock.tsx +24 -0
  50. package/dist/templates/nextjs/src/lib/component-props/index.ts +23 -0
  51. package/dist/templates/nextjs/src/lib/data-fetcher.ts +16 -0
  52. package/dist/templates/nextjs/src/lib/dictionary-service-factory.ts +30 -0
  53. package/dist/templates/nextjs/src/lib/layout-service-factory.ts +25 -0
  54. package/dist/templates/nextjs/src/lib/next-config/plugins/graphql.js +32 -0
  55. package/dist/templates/nextjs/src/lib/next-config/plugins/monorepo.js +27 -0
  56. package/dist/templates/nextjs/src/lib/page-props-factory/index.ts +50 -0
  57. package/dist/templates/nextjs/src/lib/page-props-factory/plugins/componentProps.ts +38 -0
  58. package/dist/templates/nextjs/src/lib/page-props-factory/plugins/normalMode.ts +75 -0
  59. package/dist/templates/nextjs/src/lib/page-props-factory/plugins/previewMode.ts +27 -0
  60. package/dist/templates/nextjs/src/lib/page-props.ts +16 -0
  61. package/dist/templates/nextjs/src/lib/sitemap-fetcher/index.ts +25 -0
  62. package/dist/templates/nextjs/src/lib/sitemap-fetcher/plugins/graphqlSitemapService.ts +36 -0
  63. package/dist/templates/nextjs/src/pages/404.tsx +5 -0
  64. package/dist/templates/nextjs/src/pages/[[...path]].tsx +91 -0
  65. package/dist/templates/nextjs/src/pages/_app.tsx +19 -0
  66. package/dist/templates/nextjs/src/pages/_error.tsx +35 -0
  67. package/dist/templates/nextjs/src/pages/api/editing/data/[key].ts +24 -0
  68. package/dist/templates/nextjs/src/pages/api/editing/render.ts +30 -0
  69. package/dist/templates/nextjs/src/temp/.gitignore +3 -0
  70. package/dist/templates/nextjs/src/temp/GraphQLIntrospectionResult.json +22216 -0
  71. package/dist/templates/nextjs/tsconfig.json +42 -0
  72. package/dist/templates/nextjs/tsconfig.scripts.json +6 -0
  73. package/dist/templates/nextjs-styleguide/data/component-content/.gitignore +0 -0
  74. package/dist/templates/nextjs-styleguide/data/component-content/Styleguide/ContentReuse/LoremIpsumContentBlock/en.yml +9 -0
  75. package/dist/templates/nextjs-styleguide/data/content/.gitignore +0 -0
  76. package/dist/templates/nextjs-styleguide/data/content/Styleguide/ContentListField/Item1/en.yml +6 -0
  77. package/dist/templates/nextjs-styleguide/data/content/Styleguide/ContentListField/Item2/en.yml +6 -0
  78. package/dist/templates/nextjs-styleguide/data/content/Styleguide/ItemLinkField/Item1/en.yml +6 -0
  79. package/dist/templates/nextjs-styleguide/data/content/Styleguide/ItemLinkField/Item2/en.yml +6 -0
  80. package/dist/templates/nextjs-styleguide/data/dictionary/en.yml +4 -0
  81. package/dist/templates/nextjs-styleguide/data/dictionary/{{language}}.yml +4 -0
  82. package/dist/templates/nextjs-styleguide/data/media/files/jss.pdf +0 -0
  83. package/dist/templates/nextjs-styleguide/data/media/img/jss_logo.png +0 -0
  84. package/dist/templates/nextjs-styleguide/data/media/img/sc_logo.png +0 -0
  85. package/dist/templates/nextjs-styleguide/data/routes/en.yml +63 -0
  86. package/dist/templates/nextjs-styleguide/data/routes/graphql/en.yml +27 -0
  87. package/dist/templates/nextjs-styleguide/data/routes/graphql/sample-1/en.yml +9 -0
  88. package/dist/templates/nextjs-styleguide/data/routes/graphql/sample-2/en.yml +9 -0
  89. package/dist/templates/nextjs-styleguide/data/routes/styleguide/custom-route-type/en.yml +12 -0
  90. package/dist/templates/nextjs-styleguide/data/routes/styleguide/en.yml +246 -0
  91. package/dist/templates/nextjs-styleguide/data/routes/styleguide/{{language}}.yml +25 -0
  92. package/dist/templates/nextjs-styleguide/data/routes/{{language}}.yml +4 -0
  93. package/dist/templates/nextjs-styleguide/package.json +13 -0
  94. package/dist/templates/nextjs-styleguide/scripts/disconnected-mode-proxy.ts +48 -0
  95. package/dist/templates/nextjs-styleguide/scripts/scaffold-component.ts +130 -0
  96. package/dist/templates/nextjs-styleguide/scripts/templates/component-manifest.ts +30 -0
  97. package/dist/templates/nextjs-styleguide/sitecore/definitions/component-content.sitecore.ts +117 -0
  98. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/ContentBlock.sitecore.ts +19 -0
  99. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Checkbox.sitecore.ts +23 -0
  100. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-ContentList.sitecore.ts +32 -0
  101. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Custom.sitecore.ts +23 -0
  102. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Date.sitecore.ts +23 -0
  103. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-File.sitecore.ts +20 -0
  104. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Image.sitecore.ts +23 -0
  105. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-ItemLink.sitecore.ts +32 -0
  106. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Link.sitecore.ts +25 -0
  107. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Number.sitecore.ts +20 -0
  108. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-RichText.sitecore.ts +28 -0
  109. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/fields/Styleguide-FieldUsage-Text.sitecore.ts +28 -0
  110. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/graphql/GraphQL-ConnectedDemo.sitecore.ts +18 -0
  111. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/graphql/GraphQL-IntegratedDemo.sitecore.graphql +71 -0
  112. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/graphql/GraphQL-IntegratedDemo.sitecore.ts +25 -0
  113. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/graphql/GraphQL-Layout.sitecore.ts +15 -0
  114. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-ComponentParams.sitecore.ts +20 -0
  115. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-CustomRouteType.sitecore.ts +31 -0
  116. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Layout-Reuse.sitecore.ts +20 -0
  117. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Layout-Tabs-Tab.sitecore.ts +23 -0
  118. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Layout-Tabs.sitecore.ts +20 -0
  119. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Layout.sitecore.ts +15 -0
  120. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Multilingual.sitecore.ts +26 -0
  121. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-RouteFields.sitecore.ts +22 -0
  122. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Section.sitecore.ts +16 -0
  123. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-SitecoreContext.sitecore.ts +19 -0
  124. package/dist/templates/nextjs-styleguide/sitecore/definitions/components/styleguide/Styleguide-Tracking.sitecore.ts +19 -0
  125. package/dist/templates/nextjs-styleguide/sitecore/definitions/config.js +5 -0
  126. package/dist/templates/nextjs-styleguide/sitecore/definitions/content.sitecore.ts +117 -0
  127. package/dist/templates/nextjs-styleguide/sitecore/definitions/dictionary.sitecore.ts +52 -0
  128. package/dist/templates/nextjs-styleguide/sitecore/definitions/placeholders.sitecore.ts +21 -0
  129. package/dist/templates/nextjs-styleguide/sitecore/definitions/routes.sitecore.ts +88 -0
  130. package/dist/templates/nextjs-styleguide/sitecore/definitions/templates/Styleguide-ContentList-Template.sitecore.ts +12 -0
  131. package/dist/templates/nextjs-styleguide/sitecore/definitions/templates/Styleguide-Explanatory-Component.sitecore.ts +20 -0
  132. package/dist/templates/nextjs-styleguide/sitecore/definitions/templates/Styleguide-ItemLink-Template.sitecore.ts +13 -0
  133. package/dist/templates/nextjs-styleguide/src/Navigation.tsx +41 -0
  134. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Checkbox.tsx +52 -0
  135. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-ContentList.tsx +58 -0
  136. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Custom.tsx +25 -0
  137. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Date.tsx +55 -0
  138. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-File.tsx +30 -0
  139. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Image.tsx +58 -0
  140. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-ItemLink.tsx +50 -0
  141. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Link.tsx +46 -0
  142. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Number.tsx +34 -0
  143. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-RichText.tsx +35 -0
  144. package/dist/templates/nextjs-styleguide/src/components/fields/Styleguide-FieldUsage-Text.tsx +43 -0
  145. package/dist/templates/nextjs-styleguide/src/components/graphql/GraphQL-ConnectedDemo.dynamic.graphql +69 -0
  146. package/dist/templates/nextjs-styleguide/src/components/graphql/GraphQL-ConnectedDemo.dynamic.tsx +148 -0
  147. package/dist/templates/nextjs-styleguide/src/components/graphql/GraphQL-IntegratedDemo.tsx +130 -0
  148. package/dist/templates/nextjs-styleguide/src/components/graphql/GraphQL-Layout.tsx +37 -0
  149. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-ComponentParams.tsx +47 -0
  150. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-CustomRouteType.tsx +37 -0
  151. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Layout-Reuse.tsx +45 -0
  152. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Layout-Tabs-Tab.tsx +41 -0
  153. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Layout-Tabs.tsx +111 -0
  154. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Layout.tsx +68 -0
  155. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Multilingual.tsx +50 -0
  156. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-RouteFields.tsx +37 -0
  157. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Section.tsx +22 -0
  158. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-SitecoreContext.tsx +25 -0
  159. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Specimen.tsx +41 -0
  160. package/dist/templates/nextjs-styleguide/src/components/styleguide/Styleguide-Tracking.tsx +341 -0
  161. package/dist/templates/nextjs-styleguide/src/lib/component-props/styleguide.ts +11 -0
  162. package/dist/templates/nextjs-styleguide/src/lib/next-config/plugins/disconnected.js +43 -0
  163. package/dist/templates/nextjs-styleguide/src/lib/next-config/plugins/styleguide.js +10 -0
  164. package/dist/templates/nextjs-styleguide/src/lib/sitemap-fetcher/plugins/disconnectedSitemapService.ts +46 -0
  165. package/dist/templates/nextjs-styleguide/src/pages/_app.tsx +33 -0
  166. package/package.json +61 -0
@@ -0,0 +1,85 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { constantCase } from 'constant-case';
4
+ import packageConfig from '../package.json';
5
+
6
+ /* eslint-disable no-console */
7
+
8
+ /**
9
+ * Generate config
10
+ * The object returned from this function will be made available by importing src/temp/config.js.
11
+ * This is executed prior to the build running, so it's a way to inject environment or build config-specific
12
+ * settings as variables into the JSS app.
13
+ * NOTE! Any configs returned here will be written into the client-side JS bundle. DO NOT PUT SECRETS HERE.
14
+ * @param {object} configOverrides Keys in this object will override any equivalent global config keys.
15
+ */
16
+ export function generateConfig(configOverrides?: { [key: string]: string }): void {
17
+ const defaultConfig = {
18
+ sitecoreApiKey: 'no-api-key-set',
19
+ sitecoreApiHost: '',
20
+ jssAppName: 'Unknown',
21
+ };
22
+
23
+ // require + combine config sources
24
+ const scjssConfig = transformScJssConfig();
25
+ const packageJson = transformPackageConfig();
26
+
27
+ // Object.assign merges the objects in order, so config overrides are performed as:
28
+ // default config <-- scjssconfig.json <-- package.json <-- configOverrides
29
+ // Optional: add any other dynamic config source (e.g. environment-specific config files).
30
+ const config = Object.assign(defaultConfig, scjssConfig, packageJson, configOverrides);
31
+
32
+ // The GraphQL endpoint is an example of making a _computed_ config setting
33
+ // based on other config settings.
34
+ const computedConfig: { [key: string]: string } = {};
35
+ computedConfig.graphQLEndpoint = '`${config.sitecoreApiHost}${config.graphQLEndpointPath}`';
36
+
37
+ let configText = `/* eslint-disable */
38
+ // Do not edit this file, it is auto-generated at build time!
39
+ // See scripts/bootstrap.ts to modify the generation of this file.
40
+ const config = {};\n`;
41
+
42
+ // Set base configuration values, allowing override with environment variables
43
+ Object.keys(config).forEach((prop) => {
44
+ configText += `config.${prop} = process.env.${constantCase(prop)} || "${config[prop]}",\n`;
45
+ });
46
+ // Set computed values, allowing override with environment variables
47
+ Object.keys(computedConfig).forEach((prop) => {
48
+ configText += `config.${prop} = process.env.${constantCase(prop)} || ${
49
+ computedConfig[prop]
50
+ };\n`;
51
+ });
52
+ configText += `module.exports = config;`;
53
+
54
+ const configPath = path.resolve('src/temp/config.js');
55
+ console.log(`Writing runtime config to ${configPath}`);
56
+ fs.writeFileSync(configPath, configText, { encoding: 'utf8' });
57
+ }
58
+
59
+ function transformScJssConfig() {
60
+ // scjssconfig.json may not exist if you've never run `jss setup` (development)
61
+ // or are depending on environment variables instead (production).
62
+ let config;
63
+ try {
64
+ // eslint-disable-next-line global-require
65
+ config = require('../scjssconfig.json');
66
+ } catch (e) {
67
+ return {};
68
+ }
69
+
70
+ if (!config) return {};
71
+
72
+ return {
73
+ sitecoreApiKey: config.sitecore.apiKey,
74
+ sitecoreApiHost: config.sitecore.layoutServiceHost,
75
+ };
76
+ }
77
+
78
+ function transformPackageConfig() {
79
+ if (!packageConfig.config) return {};
80
+
81
+ return {
82
+ jssAppName: packageConfig.config.appName,
83
+ graphQLEndpointPath: packageConfig.config.graphQLEndpointPath,
84
+ };
85
+ }
@@ -0,0 +1,103 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getItems } from './utils';
4
+
5
+ /*
6
+ PLUGINS GENERATION
7
+ NOTE: pluginName: the name of the plugin in the src/lib folder
8
+ Generates the `/src/temp/{pluginName}-plugins.ts` file, which exports list of plugins
9
+
10
+ Generating the plugins is optional, and it can be maintained manually if preferred.
11
+
12
+ The default convention uses the plugin's filename (without the extension) as the first part of the component
13
+ name. For example, the file `/lib/page-props-factory/plugins/exampleName.ts` would map to plugin `exampleNamePlugin`.
14
+ This can be customized in writePlugins().
15
+ */
16
+
17
+ enum ModuleType {
18
+ CJS,
19
+ ESM,
20
+ }
21
+
22
+ interface PluginDefinition {
23
+ listPath: string;
24
+ rootPath: string;
25
+ moduleType: ModuleType;
26
+ }
27
+
28
+ interface PluginFile {
29
+ path: string;
30
+ name: string;
31
+ }
32
+
33
+ const pluginDefinitions = [
34
+ {
35
+ listPath: 'src/temp/sitemap-fetcher-plugins.ts',
36
+ rootPath: 'src/lib/sitemap-fetcher/plugins',
37
+ moduleType: ModuleType.ESM,
38
+ },
39
+ {
40
+ listPath: 'src/temp/page-props-factory-plugins.ts',
41
+ rootPath: 'src/lib/page-props-factory/plugins',
42
+ moduleType: ModuleType.ESM,
43
+ },
44
+ {
45
+ listPath: 'src/temp/next-config-plugins.js',
46
+ rootPath: 'src/lib/next-config/plugins',
47
+ moduleType: ModuleType.CJS,
48
+ },
49
+ ];
50
+
51
+ run(pluginDefinitions);
52
+
53
+ function run(definitions: PluginDefinition[]) {
54
+ definitions.forEach((definition) => {
55
+ writePlugins(definition.listPath, definition.rootPath, definition.moduleType);
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Generates the plugins file and saves it to the filesystem.
61
+ * By convention, we expect to find plugins under src/lib/{pluginName}/plugins/** (subfolders are
62
+ * searched recursively). The filename, with extension and non-word characters
63
+ * stripped, is used to identify the plugin's JavaScript module definition (for adding
64
+ * new plugin to the factory).
65
+ * Modify this function to use a different convention.
66
+ */
67
+ function writePlugins(listPath: string, rootPath: string, moduleType: ModuleType) {
68
+ const pluginName = rootPath.split('/')[2];
69
+ const plugins = getPluginList(rootPath, pluginName);
70
+ let fileContent = '';
71
+
72
+ fileContent = plugins
73
+ .map((plugin) => {
74
+ return moduleType === ModuleType.CJS
75
+ ? `exports.${plugin.name} = require('${plugin.path.replace('src/', '../')}');`
76
+ : `export { ${plugin.name} } from '${plugin.path}';`;
77
+ })
78
+ .join('\r\n')
79
+ .concat('\r\n');
80
+
81
+ if (!plugins.length) {
82
+ fileContent = moduleType === ModuleType.CJS ? 'module.exports = {};\r\n' : 'export {};\r\n';
83
+ }
84
+
85
+ const filePath = path.resolve(listPath);
86
+ console.log(`Writing ${pluginName} plugins to ${filePath}`);
87
+ fs.writeFileSync(filePath, fileContent, {
88
+ encoding: 'utf8',
89
+ });
90
+ }
91
+
92
+ function getPluginList(path: string, pluginName: string): PluginFile[] {
93
+ const plugins = getItems<PluginFile>({
94
+ path,
95
+ resolveItem: (path, name) => ({
96
+ path: `${path}/${name}`,
97
+ name: `${name.replace(/[^\w]+/g, '')}Plugin`,
98
+ }),
99
+ cb: (name) => console.debug(`Registering ${pluginName} plugin ${name}`),
100
+ });
101
+
102
+ return plugins;
103
+ }
@@ -0,0 +1,89 @@
1
+ /*
2
+ Component Scaffolding Script
3
+ This is a script that enables scaffolding a new JSS component using `jss scaffold <ComponentName>`.
4
+ The default convention is that component names must start with a capital letter, and can contain
5
+ letters, number, underscores, or dashes.
6
+
7
+ If the <ComponentName> parameter includes a path, it must be relative to the src/components folder.
8
+ For example, `jss scaffold search/SearchBox` will create a component called `SearchBox` in
9
+ `src/components/search/SearchBox.tsx`. Specifying a relative path is optional, and just providing
10
+ the name is ok.
11
+
12
+ Edit this script if you wish to use your own conventions for component storage in your JSS app.
13
+ */
14
+
15
+ /* eslint-disable no-throw-literal,no-console */
16
+
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import chalk from 'chalk';
20
+ import generateComponentSrc from './templates/component-src';
21
+
22
+ const componentRootPath = 'src/components';
23
+
24
+ // Matches component names that start with a capital letter, and contain only letters, number,
25
+ // underscores, or dashes. Optionally, the component name can be preceded by a relative path
26
+ const nameParamFormat = new RegExp(/^((?:[\w-]+\/)*)([A-Z][\w-]+)$/);
27
+ const componentArg = process.argv[2];
28
+
29
+ if (!componentArg) {
30
+ throw 'Component name was not passed. Usage: jss scaffold <ComponentName>';
31
+ }
32
+
33
+ const regExResult = nameParamFormat.exec(componentArg);
34
+
35
+ if (regExResult === null) {
36
+ throw `Component name should start with an uppercase letter and contain only letters, numbers,
37
+ dashes, or underscores. If specifying a path, it must be relative to src/components`;
38
+ }
39
+
40
+ const componentPath = regExResult[1];
41
+ const componentName = regExResult[2];
42
+ const filename = `${componentName}.tsx`;
43
+
44
+ const componentOutputPath = scaffoldFile(
45
+ componentRootPath,
46
+ generateComponentSrc(componentName),
47
+ filename
48
+ );
49
+
50
+ console.log(
51
+ chalk.green(`
52
+ Scaffolding of ${componentName} complete.
53
+ Next steps:`)
54
+ );
55
+
56
+ if (componentOutputPath) {
57
+ console.log(`* Implement the React component in ${chalk.green(componentOutputPath)}`);
58
+ }
59
+
60
+ /**
61
+ * Force to use `crlf` line endings, we are using `crlf` across the project.
62
+ * Replace: `lf` (\n), `cr` (\r)
63
+ * @param {string} content
64
+ */
65
+ function editLineEndings(content: string) {
66
+ return content.replace(/\r|\n/gm, '\r\n');
67
+ }
68
+
69
+ /**
70
+ * Creates a file relative to the specified path if the file doesn't exist. Creates directories as needed.
71
+ * @param {string} rootPath - the root path
72
+ * @param {string} fileContent - the file content
73
+ * @param {string} filename - the filename
74
+ * @returns the new file's filepath
75
+ */
76
+ function scaffoldFile(rootPath: string, fileContent: string, filename: string): string | null {
77
+ const outputDir = path.join(rootPath, componentPath);
78
+ const outputFile = path.join(outputDir, filename);
79
+
80
+ if (fs.existsSync(outputFile)) {
81
+ console.log(chalk.red(`Skipping creating ${outputFile}; already exists.`));
82
+ return null;
83
+ }
84
+
85
+ fs.mkdirSync(outputDir, { recursive: true });
86
+ fs.writeFileSync(outputFile, editLineEndings(fileContent), 'utf8');
87
+ console.log(chalk.green(`File ${outputFile} has been scaffolded.`));
88
+ return outputFile;
89
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Describes a file that represents a component definition
3
+ */
4
+ export interface ComponentFile {
5
+ path: string;
6
+ moduleName: string;
7
+ componentName: string;
8
+ }
9
+
10
+ export interface PackageDefinition {
11
+ name: string;
12
+ components: {
13
+ moduleName: string;
14
+ componentName: string;
15
+ }[];
16
+ }
17
+
18
+ const isLazyLoadingModule = (componentPath: string) => componentPath.includes('.dynamic');
19
+
20
+ const removeDynamicModuleNameEnding = (moduleName: string) =>
21
+ moduleName.replace(/\.?dynamic$/i, '');
22
+
23
+ /**
24
+ * Generates the contents of the component factory file using a predefined string template.
25
+ * @param components - the list of component files to include
26
+ * @returns component factory file contents
27
+ */
28
+ function generateComponentFactory(components: (PackageDefinition | ComponentFile)[]): string {
29
+ const componentFiles = components.filter(
30
+ (component) => (component as ComponentFile).path
31
+ ) as ComponentFile[];
32
+ const packages = components.filter(
33
+ (component) => (component as PackageDefinition).components
34
+ ) as PackageDefinition[];
35
+
36
+ const hasLazyModules = componentFiles.find((component) => isLazyLoadingModule(component.path));
37
+
38
+ return `/* eslint-disable */
39
+ // Do not edit this file, it is auto-generated at build time!
40
+ // See scripts/generate-component-factory.ts to modify the generation of this file.
41
+
42
+ ${hasLazyModules ? "import dynamic from 'next/dynamic'" : ''}
43
+
44
+ ${packages.map((pkg) => {
45
+ const list = pkg.components.map((c) => c.moduleName).join(', ');
46
+
47
+ return `import { ${list} } from '${pkg.name}'`;
48
+ })}
49
+ ${componentFiles
50
+ .map((component) => {
51
+ if (isLazyLoadingModule(component.path)) {
52
+ const moduleName = removeDynamicModuleNameEnding(component.moduleName);
53
+ return `const ${moduleName} = {
54
+ module: () => import('${component.path}'),
55
+ element: () => dynamic(${moduleName}.module)
56
+ }`;
57
+ }
58
+
59
+ return `import * as ${component.moduleName} from '${component.path}';`;
60
+ })
61
+ .join('\n')}
62
+
63
+ const components = new Map();
64
+ ${packages.map((p) =>
65
+ p.components.map(
66
+ (component) => `components.set('${component.componentName}', ${component.moduleName})`
67
+ )
68
+ )}
69
+ ${componentFiles
70
+ .map(
71
+ (component) =>
72
+ `components.set('${
73
+ isLazyLoadingModule(component.path)
74
+ ? removeDynamicModuleNameEnding(component.componentName)
75
+ : component.componentName
76
+ }', ${
77
+ isLazyLoadingModule(component.path)
78
+ ? removeDynamicModuleNameEnding(component.moduleName)
79
+ : component.moduleName
80
+ });`
81
+ )
82
+ .join('\n')}
83
+
84
+ // Next.js 'dynamic' import and JavaScript 'dynamic' import are different.
85
+ // Next.js 'dynamic(...)' returns common 'React.ComponentType' while
86
+ // 'import('...')' returns 'Promise' that will resolve module.
87
+ // componentModule uses 'import(...)' because primary usage of it to get not only 'React Component' (default export) but all named exports.
88
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports
89
+ // componentFactory uses 'dynamic(...)' because primary usage of it to render 'React Component' (default export).
90
+ // See https://nextjs.org/docs/advanced-features/dynamic-import
91
+ // At the end you will have single preloaded script for each lazy loading module.
92
+
93
+ export function componentModule(componentName: string) {
94
+ const component = components.get(componentName);
95
+
96
+ // check that component is lazy loading module
97
+ if (!component?.default && component?.module) {
98
+ // return js dynamic import
99
+ return component.module();
100
+ }
101
+
102
+ return component;
103
+ }
104
+
105
+ export function componentFactory(componentName: string) {
106
+ const component = components.get(componentName);
107
+
108
+ // check that component should be dynamically imported
109
+ if (component?.element) {
110
+ // return next.js dynamic import
111
+ return component.element();
112
+ }
113
+
114
+ return component?.default || component;
115
+ }
116
+ `;
117
+ }
118
+
119
+ export default generateComponentFactory;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Generates React boilerplate for a component under `src/components`
3
+ * @param componentName - the component name
4
+ * @returns component src boilerplate as a string
5
+ */
6
+ function generateComponentSrc(componentName: string): string {
7
+ return `import { Text, Field, withDatasourceCheck } from '@sitecore-jss/sitecore-jss-nextjs';
8
+ import { ComponentProps } from 'lib/component-props';
9
+
10
+ type ${componentName}Props = ComponentProps & {
11
+ fields: {
12
+ heading: Field<string>;
13
+ };
14
+ };
15
+
16
+ const ${componentName} = (props: ${componentName}Props): JSX.Element => (
17
+ <div>
18
+ <p>${componentName} Component</p>
19
+ <Text field={props.fields.heading} />
20
+ </div>
21
+ );
22
+
23
+ export default withDatasourceCheck()<${componentName}Props>(${componentName});
24
+ `;
25
+ }
26
+
27
+ export default generateComponentSrc;
@@ -0,0 +1,59 @@
1
+ import fs from 'fs';
2
+ import chokidar from 'chokidar';
3
+
4
+ /**
5
+ * Run watch mode, watching on @var rootPath
6
+ */
7
+ export function watchItems(rootPath: string, cb: () => void): void {
8
+ chokidar
9
+ .watch(rootPath, { ignoreInitial: true, awaitWriteFinish: true })
10
+ .on('add', cb)
11
+ .on('unlink', cb);
12
+ }
13
+
14
+ /**
15
+ * Using @var path find all files recursively and generate output using @var resolveItem by calling it for each file
16
+ * @param path plugins path
17
+ * @param resolveItem will resolve item in required data format
18
+ * @param cb will be called when new item is found
19
+ * @param fileFormat Matches specific files
20
+ * @returns {Item[]} items
21
+ */
22
+ export function getItems<Item>(settings: {
23
+ path: string;
24
+ resolveItem: (path: string, name: string) => Item;
25
+ cb?: (name: string) => void;
26
+ fileFormat?: RegExp;
27
+ }): Item[] {
28
+ const { path, resolveItem, cb, fileFormat = new RegExp(/(.+)(?<!\.d)\.[jt]sx?$/) } = settings;
29
+ const items: Item[] = [];
30
+ const folders: fs.Dirent[] = [];
31
+
32
+ if (!fs.existsSync(path)) return [];
33
+
34
+ fs.readdirSync(path, { withFileTypes: true }).forEach((item) => {
35
+ if (item.isDirectory()) {
36
+ folders.push(item);
37
+ }
38
+
39
+ if (fileFormat.test(item.name)) {
40
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
41
+ const name = item.name.match(fileFormat)![1];
42
+ items.push(resolveItem(path, name));
43
+ cb && cb(name);
44
+ }
45
+ });
46
+
47
+ for (const folder of folders) {
48
+ items.push(
49
+ ...getItems<Item>({
50
+ path: `${path}/${folder.name}`,
51
+ resolveItem,
52
+ cb,
53
+ fileFormat,
54
+ })
55
+ );
56
+ }
57
+
58
+ return items;
59
+ }
@@ -0,0 +1,151 @@
1
+ <!--
2
+ JSS Sitecore Configuration Patch File
3
+
4
+ This configuration file registers the JSS site with Sitecore, and configures the Layout Service
5
+ to work with it. Config patches need to be deployed to the Sitecore server.
6
+
7
+ Normally `jss deploy config` can do this for local development. To manually deploy, or to deploy via CI,
8
+ this file can be placed in the `App_Config/Include` folder, or a subfolder of it, within the Sitecore site.
9
+ -->
10
+ <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
11
+ <sitecore>
12
+ <!--
13
+ Note that if any of these settings are enabled, they will apply to the entire Sitecore instance. If there are
14
+ multiple sites defined in the instances, the settings will affect all of them.
15
+ -->
16
+ <settings>
17
+ <!--
18
+ ANALYTICS FORWARDED REQUEST HEADER
19
+ When using Next.js SSR routes, Layout Service requests will send the original IP address of the client on the 'X-Forwarded-For' header.
20
+ This setting tells Sitecore to read the forwarded header, thus making analytics track the correct original client IP address.
21
+
22
+ <setting name="Analytics.ForwardedRequestHttpHeader" set:value="X-Forwarded-For" />
23
+ -->
24
+
25
+ <!--
26
+ ANALYTICS DISABLE ROBOT DETECTION
27
+ During development, activity will flag us as a robot.
28
+ These settings will enable tracking of robot activity for ease of testing (development ONLY).
29
+
30
+ <setting name="Analytics.AutoDetectBots" set:value="false" />
31
+ <setting name="Analytics.Robots.IgnoreRobots" set:value="false" />
32
+ -->
33
+
34
+ <!--
35
+ JSS EDITING SECRET
36
+ To secure the Experience Editor endpoint exposed by your Next.js app (see `serverSideRenderingEngineEndpointUrl` below),
37
+ a secret token is used. This is taken from an env variable by default, but could be patched and set directly by uncommenting.
38
+ This (server-side) value must match your client-side value, which is configured by the JSS_EDITING_SECRET env variable (see the Next.js .env file).
39
+ We recommend an alphanumeric value of at least 16 characters.
40
+
41
+ <setting name="JavaScriptServices.ViewEngine.Http.JssEditingSecret" value="" />
42
+ -->
43
+
44
+ <!--
45
+ LANGUAGE FALLBACK
46
+ These settings enable item-level language fallback for JSS apps that use Experience Edge.
47
+ In addition to these settings, ensure that Language Fallback is configured on the Language
48
+ item in Sitecore, and that items in the content tree have "Enable Item Fallback" field set
49
+ to true. Other settings related to item-level language fallback that may exist in Sitecore
50
+ configuration will not impact Edge functionality.
51
+
52
+ <setting name="ExperienceEdge.EnableItemLanguageFallback" value="true"/>
53
+ <setting name="ExperienceEdge.EnableFieldLanguageFallback" value="true"/>
54
+ -->
55
+ </settings>
56
+ <sites>
57
+ <!--
58
+ JSS Site Registration
59
+ This configures the site with Sitecore - i.e. host headers, item paths.
60
+ If your JSS app lives within an existing Sitecore site, this may not be necessary.
61
+
62
+ IMPORTANT: JSS sites ship in 'live mode', which makes development and testing easy,
63
+ but disables workflow and publishing. Before going to production, change the `database`
64
+ below to `web` instead of `master`.
65
+ -->
66
+ <site patch:before="site[@name='website']"
67
+ inherits="website"
68
+ name="<%- appName %>"
69
+ hostName="<%- hostName.replace(/^https?:\/\//, '') %>"
70
+ rootPath="/sitecore/content/<%- appName %>"
71
+ startItem="/home"
72
+ database="master" />
73
+ </sites>
74
+ <javaScriptServices>
75
+ <apps>
76
+ <!--
77
+ JSS App Registration
78
+ The JSS app needs to be registered in order to support layout service and import services.
79
+
80
+ There are many available attributes, and they inherit the defaults if not explicitly specified here.
81
+ Defaults are defined in `/App_Config/Sitecore/JavaScriptServices/Sitecore.JavaScriptServices.Apps.config`
82
+
83
+ NOTE: graphQLEndpoint enables _Integrated GraphQL_. If not using integrated GraphQL, it can be removed.
84
+
85
+ NOTE: layoutServiceConfiguration should be set to "default" when using GraphQL Edge schema.
86
+ When using integrated GraphQL with Edge schema, a $language value is injected
87
+ since language is required in all Edge queries. "jss" configuration does not do this (which is backwards
88
+ compatible with JSS versions < 18.0.0).
89
+ -->
90
+ <app name="<%- appName %>"
91
+ layoutServiceConfiguration="default"
92
+ sitecorePath="/sitecore/content/<%- appName %>"
93
+ useLanguageSpecificLayout="true"
94
+ graphQLEndpoint="/sitecore/api/graph/edge"
95
+ inherits="defaults"
96
+ serverSideRenderingEngine="http"
97
+ serverSideRenderingEngineEndpointUrl="http://localhost:3000/api/editing/render"
98
+ serverSideRenderingEngineApplicationUrl="http://localhost:3000"
99
+ />
100
+ </apps>
101
+ <!--
102
+ IMAGE RESIZING WHITELIST
103
+ Using Sitecore server-side media resizing (i.e. the `imageParams` or `srcSet` props on the `<Image/>` helper component)
104
+ could expose your Sitecore server to a denial-of-service attack by rescaling an image with many arbitrary dimensions.
105
+ In JSS resizing param sets that are unknown are rejected by a whitelist.
106
+
107
+ Sets of image sizing parameters that are used in app components must be whitelisted here.
108
+ If a param set is not whitelisted, the image will be returned _without resizing_.
109
+
110
+ To determine the image parameters being used, look at the query string on the `src` of the rendered image, i.e. '/img.jpg?mw=100&h=72' -> mw=100,h=72
111
+ Note: the parameter sets defined here are comma-delimited (,) instead of &-delimited like the query string. Multiple sets are endline-delimited.
112
+ -->
113
+ <allowedMediaParams>
114
+ <!-- XML element name is arbitary, useful for organizing and patching -->
115
+ <styleguide-image-sample>
116
+ mw=100,mh=50
117
+ </styleguide-image-sample>
118
+ <styleguide-image-sample-adaptive>
119
+ mw=300
120
+ mw=100
121
+ </styleguide-image-sample-adaptive>
122
+ </allowedMediaParams>
123
+ </javaScriptServices>
124
+ <!--
125
+ Media URLs resolving
126
+ Tells Sitecore to not include the Sitecore server URL as part of the media requests, so that they are instead routed through Next.js rewrites (see next.config.js).
127
+ This eliminates exposing the Sitecore server publicly.
128
+
129
+ "default" configuration is used for Sitecore GraphQL Edge requests.
130
+ "jss" configuration is used for Sitecore Layout Service REST requests.
131
+ -->
132
+ <layoutService>
133
+ <configurations>
134
+ <config name="default">
135
+ <rendering>
136
+ <renderingContentsResolver>
137
+ <IncludeServerUrlInMediaUrls>false</IncludeServerUrlInMediaUrls>
138
+ </renderingContentsResolver>
139
+ </rendering>
140
+ </config>
141
+ <config name="jss">
142
+ <rendering>
143
+ <renderingContentsResolver>
144
+ <IncludeServerUrlInMediaUrls>false</IncludeServerUrlInMediaUrls>
145
+ </renderingContentsResolver>
146
+ </rendering>
147
+ </config>
148
+ </configurations>
149
+ </layoutService>
150
+ </sitecore>
151
+ </configuration>
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import Head from 'next/head';
3
+ import deepEqual from 'deep-equal';
4
+ import {
5
+ Placeholder,
6
+ VisitorIdentification,
7
+ withSitecoreContext,
8
+ getPublicUrl,
9
+ SitecoreContextValue,
10
+ } from '@sitecore-jss/sitecore-jss-nextjs';
11
+ import Navigation from 'src/Navigation';
12
+
13
+ // Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor.
14
+ // If you're not supporting the Experience Editor, you can remove this.
15
+ const publicUrl = getPublicUrl();
16
+
17
+ interface LayoutProps {
18
+ sitecoreContext: SitecoreContextValue;
19
+ }
20
+
21
+ const Layout = ({ sitecoreContext: { route } }: LayoutProps): JSX.Element => {
22
+ return (
23
+ <>
24
+ <Head>
25
+ <title>{route?.fields?.pageTitle?.value || 'Page'}</title>
26
+ <link rel="icon" href={`${publicUrl}/favicon.ico`} />
27
+ </Head>
28
+
29
+ {/*
30
+ VisitorIdentification is necessary for Sitecore Analytics to determine if the visitor is a robot.
31
+ If Sitecore XP (with xConnect/xDB) is used, this is required or else analytics will not be collected for the JSS app.
32
+ For XM (CMS-only) apps, this should be removed.
33
+
34
+ VI detection only runs once for a given analytics ID, so this is not a recurring operation once cookies are established.
35
+ */}
36
+ <VisitorIdentification />
37
+
38
+ <Navigation />
39
+ {/* root placeholder for the app, which we add components to using route data */}
40
+ <div className="container">
41
+ {route && <Placeholder name="<%- appPrefix ? `${appName}-` : '' %>jss-main" rendering={route} />}
42
+ </div>
43
+ </>
44
+ );
45
+ };
46
+
47
+ const propsAreEqual = (prevProps: LayoutProps, nextProps: LayoutProps) => {
48
+ if (deepEqual(prevProps.sitecoreContext.route, nextProps.sitecoreContext.route)) return true;
49
+
50
+ return false;
51
+ };
52
+
53
+ export default withSitecoreContext()(React.memo(Layout, propsAreEqual));