apostrophe 3.16.0 → 3.17.0

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 (39) hide show
  1. package/.eslintignore +3 -1
  2. package/CHANGELOG.md +23 -1
  3. package/index.js +5 -4
  4. package/lib/moog.js +14 -1
  5. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +3 -1
  6. package/modules/@apostrophecms/asset/index.js +213 -101
  7. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +3 -3
  8. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +30 -12
  9. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.es5.js +1 -1
  10. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +6 -2
  11. package/modules/@apostrophecms/asset/lib/webpack/utils.js +266 -0
  12. package/modules/@apostrophecms/asset/views/scripts.html +1 -0
  13. package/modules/@apostrophecms/asset/views/stylesheets.html +1 -0
  14. package/modules/@apostrophecms/doc/index.js +64 -0
  15. package/modules/@apostrophecms/doc-type/index.js +35 -0
  16. package/modules/@apostrophecms/i18n/i18n/en.json +2 -0
  17. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +7 -0
  18. package/modules/@apostrophecms/login/index.js +4 -0
  19. package/modules/@apostrophecms/schema/index.js +40 -49
  20. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +67 -0
  21. package/modules/@apostrophecms/template/index.js +14 -5
  22. package/modules/@apostrophecms/template/lib/bundlesLoader.js +158 -0
  23. package/modules/@apostrophecms/template/views/outerLayoutBase.html +6 -0
  24. package/modules/@apostrophecms/widget-type/index.js +21 -0
  25. package/package.json +1 -1
  26. package/test/areas.js +2 -1
  27. package/test/assets.js +307 -3
  28. package/test/docs.js +67 -2
  29. package/test/modules/bundle/index.js +7 -0
  30. package/test/modules/bundle-page/index.js +32 -0
  31. package/test/modules/bundle-page/ui/src/extra.js +1 -0
  32. package/test/modules/bundle-page/ui/src/extra.scss +0 -0
  33. package/test/modules/bundle-page/views/index.html +9 -0
  34. package/test/modules/bundle-page/views/show.html +10 -0
  35. package/test/modules/bundle-widget/index.js +27 -0
  36. package/test/modules/bundle-widget/ui/src/extra2.js +1 -0
  37. package/test/modules/bundle-widget/views/widget.html +1 -0
  38. package/test/pieces.js +38 -12
  39. package/test/public/static-test.txt +0 -1
package/.eslintignore CHANGED
@@ -1,3 +1,5 @@
1
1
  **/vendor/**/*.js
2
2
  **/blueimp/**/*.js
3
- **/node_modules
3
+ **/node_modules
4
+ test/public
5
+ test/apos-build
package/CHANGELOG.md CHANGED
@@ -1,12 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ # 3.17.0 (2022-03-31)
4
+
5
+ ### Adds
6
+
7
+ * Full support for the [`object` field type](https://v3.docs.apostrophecms.org/reference/field-types/object.html), which works just like `array` but stores just one sub-object as a property, rather than an array of objects.
8
+ * To help find documents that reference related ones via `relationship` fields, implement backlinks of related documents by adding a `relatedReverseIds` field to them and keeping it up to date. There is no UI based on this feature yet but it will permit various useful features in the near future.
9
+ * Adds possibility for modules to [extend the webpack configuration](https://v3.docs.apostrophecms.org/guide/webpack.html).
10
+ * Adds possibility for modules to [add extra frontend bundles for scss and js](https://v3.docs.apostrophecms.org/guide/webpack.html). This is useful when the `ui/src` build would otherwise be very large due to code used on rarely accessed pages.
11
+ * Loads the right bundles on the right pages depending on the page template and the loaded widgets. Logged-in users have all the bundles on every page, because they might introduce widgets at any time.
12
+
13
+ ### Fixes
14
+
15
+ * Apostrophe's webpack build now works properly when developing code that imports module-specific npm dependencies from `ui/src` or `ui/apos` when using `npm link` to develop the module in question.
16
+ * The `es5: true` option to `@apostrophecms/asset` works again.
17
+
18
+ # 3.16.1 (2022-03-21)
19
+
20
+ ### Fixes
21
+
22
+ * Fixes a bug in the new `Cache-Control` support introduced by 3.16.0 in which we get the logged-out homepage right after logging in. This issue only came into play if the new caching options were enabled.
23
+
3
24
  ## 3.16.0 (2022-03-18)
4
25
 
5
26
  ### Adds
6
27
 
7
- * Offers a simple way to set a Cache-Control max-age for Apostrophe page and GET REST API responses for pieces and pages.
28
+ * Offers a simple way to set a Cache-Control max-age for Apostrophe page and GET REST API responses for pieces and pages. [See the documentation for more information](https://v3.docs.apostrophecms.org/guide/caching.html).
8
29
  * API keys and bearer tokens "win" over session cookies when both are present. Since API keys and bearer tokens are explicitly added to the request at hand, it never makes sense to ignore them in favor of a cookie, which is implicit. This also simplifies automated testing.
9
30
  * `data-apos-test=""` selectors for certain elements frequently selected in QA tests, such as `data-apos-test="adminBar"`.
31
+ * Offer a simple way to set a Cache-Control max-age for Apostrophe page and GET REST API responses for pieces and pages.
10
32
  * To speed up functional tests, an `insecurePasswords` option has been added to the login module. This option is deliberately named to discourage use for any purpose other than functional tests in which repeated password hashing would unduly limit performance. Normally password hashing is intentionally difficult to slow down brute force attacks, especially if a database is compromised.
11
33
 
12
34
  ### Fixes
package/index.js CHANGED
@@ -291,11 +291,11 @@ module.exports = async function(options) {
291
291
  ${self.localModules}
292
292
 
293
293
  must export an object containing configuration for Apostrophe modules.
294
-
294
+
295
295
  The file:
296
-
296
+
297
297
  ${config}
298
-
298
+
299
299
  did not parse.
300
300
  `);
301
301
  throw e;
@@ -441,7 +441,8 @@ module.exports = async function(options) {
441
441
  'queries',
442
442
  'extendQueries',
443
443
  'icons',
444
- 'i18n'
444
+ 'i18n',
445
+ 'webpack'
445
446
  ]
446
447
  });
447
448
 
package/lib/moog.js CHANGED
@@ -134,7 +134,20 @@ module.exports = function(options) {
134
134
  self.options.sections = self.options.sections || [];
135
135
  self.options.unparsedSections = self.options.unparsedSections || [];
136
136
 
137
- const validKeys = [ '__meta', 'options', 'cascades', 'beforeSuperClass', 'init', 'afterAllSections', 'extend', 'improve', 'methods', 'extendMethods', 'instantiate', 'bundle' ]
137
+ const validKeys = [
138
+ '__meta',
139
+ 'options',
140
+ 'cascades',
141
+ 'beforeSuperClass',
142
+ 'init',
143
+ 'afterAllSections',
144
+ 'extend',
145
+ 'improve',
146
+ 'methods',
147
+ 'extendMethods',
148
+ 'instantiate',
149
+ 'bundle'
150
+ ]
138
151
  .concat(self.options.sections)
139
152
  .concat(self.options.sections.map(getExtendKey))
140
153
  .concat(self.options.unparsedSections)
@@ -363,7 +363,7 @@ export default {
363
363
  index
364
364
  });
365
365
  },
366
- // Regenerate all array item, area and widget ids so they are considered
366
+ // Regenerate all array item, area, object and widget ids so they are considered
367
367
  // new. Useful when copying a widget with nested content.
368
368
  regenerateIds(schema, object) {
369
369
  object._id = cuid();
@@ -372,6 +372,8 @@ export default {
372
372
  for (const item of (object[field.name] || [])) {
373
373
  this.regenerateIds(field.schema, item);
374
374
  }
375
+ } else if (field.type === 'object') {
376
+ this.regenerateIds(field.schema, object[field.name] || {});
375
377
  } else if (field.type === 'area') {
376
378
  if (object[field.name]) {
377
379
  object[field.name]._id = cuid();
@@ -6,6 +6,15 @@ const globalIcons = require('./lib/globalIcons');
6
6
  const path = require('path');
7
7
  const express = require('express');
8
8
  const { stripIndent } = require('common-tags');
9
+ const { merge: webpackMerge } = require('webpack-merge');
10
+ const cuid = require('cuid');
11
+ const {
12
+ checkModulesWebpackConfig,
13
+ getWebpackExtensions,
14
+ fillExtraBundles,
15
+ getBundlesNames,
16
+ writeBundlesImportFiles
17
+ } = require('./lib/webpack/utils');
9
18
 
10
19
  module.exports = {
11
20
 
@@ -22,13 +31,22 @@ module.exports = {
22
31
  refreshOnRestart: false
23
32
  },
24
33
 
25
- init(self) {
34
+ async init(self) {
26
35
  self.restartId = self.apos.util.generateId();
27
36
  self.iconMap = {
28
37
  ...globalIcons
29
38
  };
30
39
  self.configureBuilds();
31
40
  self.initUploadfs();
41
+
42
+ const { extensions, verifiedBundles } = await getWebpackExtensions({
43
+ getMetadata: self.apos.synth.getMetadata,
44
+ modulesToInstantiate: self.apos.modulesToBeInstantiated()
45
+ });
46
+
47
+ self.extraBundles = fillExtraBundles(verifiedBundles);
48
+ self.webpackExtensions = extensions;
49
+ self.verifiedBundles = verifiedBundles;
32
50
  },
33
51
  handlers (self) {
34
52
  return {
@@ -42,12 +60,18 @@ module.exports = {
42
60
  // Or if we've set an app option to skip the auto build
43
61
  self.apos.options.autoBuild !== false
44
62
  ) {
63
+
64
+ checkModulesWebpackConfig(self.apos.modules, self.apos.task.getReq().t);
45
65
  // If starting up normally, run the build task, checking if we
46
66
  // really need to update the apos build
47
67
  await self.apos.task.invoke('@apostrophecms/asset:build', {
48
68
  'check-apos-build': true
49
69
  });
50
70
  }
71
+ },
72
+ injectAssetsPlaceholders() {
73
+ self.apos.template.prepend('head', '@apostrophecms/asset:stylesheets');
74
+ self.apos.template.append('body', '@apostrophecms/asset:scripts');
51
75
  }
52
76
  },
53
77
  'apostrophe:destroy': {
@@ -59,6 +83,28 @@ module.exports = {
59
83
  }
60
84
  };
61
85
  },
86
+ components(self) {
87
+ return {
88
+ scripts(req, data) {
89
+ const placeholder = `[scripts-placeholder:${cuid()}]`;
90
+
91
+ req.scriptsPlaceholder = placeholder;
92
+
93
+ return {
94
+ placeholder
95
+ };
96
+ },
97
+ stylesheets(req, data) {
98
+ const placeholder = `[stylesheets-placeholder:${cuid()}]`;
99
+
100
+ req.stylesheetsPlaceholder = placeholder;
101
+
102
+ return {
103
+ placeholder
104
+ };
105
+ }
106
+ };
107
+ },
62
108
  tasks(self) {
63
109
  return {
64
110
  build: {
@@ -69,6 +115,8 @@ module.exports = {
69
115
  const namespace = self.getNamespace();
70
116
  const buildDir = `${self.apos.rootDir}/apos-build/${namespace}`;
71
117
  const bundleDir = `${self.apos.rootDir}/public/apos-frontend/${namespace}`;
118
+ const modulesToInstantiate = self.apos.modulesToBeInstantiated();
119
+
72
120
  // Don't clutter up with previous builds.
73
121
  await fs.remove(buildDir);
74
122
  await fs.mkdirp(buildDir);
@@ -120,7 +168,10 @@ module.exports = {
120
168
 
121
169
  if (rebuild) {
122
170
  await fs.mkdirp(bundleDir);
123
- await build(name, options);
171
+ await build({
172
+ name,
173
+ options
174
+ });
124
175
  }
125
176
  }
126
177
 
@@ -128,16 +179,19 @@ module.exports = {
128
179
  // just `public` and `apos`) by examining those specified as
129
180
  // targets for the various builds
130
181
  const scenes = [ ...new Set(Object.values(self.builds).map(options => options.scenes).flat()) ];
131
- let deployFiles = [];
132
- for (const scene of scenes) {
133
- deployFiles = [ ...deployFiles, ...merge(scene) ];
134
- }
182
+
135
183
  // enumerate public assets and include them in deployment if appropriate
136
184
  const publicAssets = glob.sync('modules/**/*', {
137
185
  cwd: bundleDir,
138
186
  mark: true
139
187
  }).filter(match => !match.endsWith('/'));
140
- deployFiles = [ ...deployFiles, ...publicAssets ];
188
+
189
+ const deployFiles = [
190
+ ...publicAssets,
191
+ ...merge(scenes),
192
+ ...getBundlesNames(self.extraBundles, self.options.es5)
193
+ ];
194
+
141
195
  await deploy(deployFiles);
142
196
 
143
197
  if (process.env.APOS_BUNDLE_ANALYZER) {
@@ -154,7 +208,7 @@ module.exports = {
154
208
  const directories = {};
155
209
  // Most other modules are not actually instantiated yet, but
156
210
  // we can access their metadata, which is sufficient
157
- for (const name of self.apos.modulesToBeInstantiated()) {
211
+ for (const name of modulesToInstantiate) {
158
212
  const ancestorDirectories = [];
159
213
  const metadata = self.apos.synth.getMetadata(name);
160
214
  for (const entry of metadata.__meta.chain) {
@@ -181,7 +235,9 @@ module.exports = {
181
235
  }
182
236
  }
183
237
 
184
- async function build(name, options) {
238
+ async function build({
239
+ name, options
240
+ }) {
185
241
  self.apos.util.log(req.t('apostrophe:assetTypeBuilding', {
186
242
  label: req.t(options.label)
187
243
  }));
@@ -203,6 +259,7 @@ module.exports = {
203
259
  importSuffix: 'App'
204
260
  });
205
261
  }
262
+
206
263
  if (options.index) {
207
264
  indexJsImports = getImports(source, 'index.js', {
208
265
  invokeApps: true,
@@ -219,29 +276,16 @@ module.exports = {
219
276
  if (options.webpack) {
220
277
  const importFile = `${buildDir}/${name}-import.js`;
221
278
 
222
- fs.writeFileSync(importFile, (options.prologue || '') + stripIndent`
223
- ${(iconImports && iconImports.importCode) || ''}
224
- ${(iconImports && iconImports.registerCode) || ''}
225
- ${(componentImports && componentImports.importCode) || ''}
226
- ${(tiptapExtensionImports && tiptapExtensionImports.importCode) || ''}
227
- ${(appImports && appImports.importCode) || ''}
228
- ${(indexJsImports && indexJsImports.importCode) || ''}
229
- ${(indexSassImports && indexSassImports.importCode) || ''}
230
- ${(iconImports && iconImports.registerCode) || ''}
231
- ${(componentImports && componentImports.registerCode) || ''}
232
- ${(tiptapExtensionImports && tiptapExtensionImports.registerCode) || ''}
233
- ` +
234
- (appImports ? stripIndent`
235
- setTimeout(() => {
236
- ${appImports.invokeCode}
237
- }, 0);
238
- ` : '') +
239
- // No delay on these, they expect to run early like ui/public code
240
- // and the first ones invoked set up expected stuff like apos.http
241
- (indexJsImports ? stripIndent`
242
- ${indexJsImports.invokeCode}
243
- ` : '')
244
- );
279
+ writeImportFile({
280
+ importFile,
281
+ prologue: options.prologue,
282
+ icon: iconImports,
283
+ components: componentImports,
284
+ tiptap: tiptapExtensionImports,
285
+ app: appImports,
286
+ indexJs: indexJsImports,
287
+ indexSass: indexSassImports
288
+ });
245
289
 
246
290
  const outputFilename = `${name}-build.js`;
247
291
  // Remove previous build artifacts, as some pipelines won't build all artifacts
@@ -251,13 +295,29 @@ module.exports = {
251
295
  fs.removeSync(cssPath);
252
296
  const webpack = Promise.promisify(webpackModule);
253
297
  const webpackBaseConfig = require(`./lib/webpack/${name}/webpack.config`);
298
+
299
+ const webpackExtraBundles = writeBundlesImportFiles({
300
+ name,
301
+ buildDir,
302
+ mainBundleName: outputFilename.replace('.js', ''),
303
+ verifiedBundles: self.verifiedBundles,
304
+ getImportFileOutput,
305
+ writeImportFile
306
+ });
307
+
254
308
  const webpackInstanceConfig = webpackBaseConfig({
255
309
  importFile,
256
310
  modulesDir,
257
311
  outputPath: bundleDir,
258
- outputFilename
312
+ outputFilename,
313
+ bundles: webpackExtraBundles
259
314
  }, self.apos);
260
- const result = await webpack(webpackInstanceConfig);
315
+
316
+ const webpackInstanceConfigMerged = self.webpackExtensions
317
+ ? webpackMerge(webpackInstanceConfig, ...Object.values(self.webpackExtensions))
318
+ : webpackInstanceConfig;
319
+
320
+ const result = await webpack(webpackInstanceConfigMerged);
261
321
  if (result.compilation.errors.length) {
262
322
  // Throwing a string is appropriate in a command line task
263
323
  throw cleanErrors(result.toString('errors'));
@@ -285,7 +345,7 @@ module.exports = {
285
345
  //
286
346
  // Of course, developers can push an "public" asset that is
287
347
  // the output of an ES6 pipeline.
288
- const publicImports = getImports(name, '*.js', { });
348
+ const publicImports = getImports(name, '*.js');
289
349
  fs.writeFileSync(`${bundleDir}/${name}-build.js`,
290
350
  (((options.prologue || '') + '\n') || '') +
291
351
  publicImports.paths.map(path => {
@@ -294,7 +354,7 @@ module.exports = {
294
354
  );
295
355
  }
296
356
  if (options.outputs.includes('css')) {
297
- const publicImports = getImports(name, '*.css', { });
357
+ const publicImports = getImports(name, '*.css');
298
358
  fs.writeFileSync(`${bundleDir}/${name}-build.css`,
299
359
  publicImports.paths.map(path => {
300
360
  return self.filterCss(fs.readFileSync(path, 'utf8'), {
@@ -309,9 +369,43 @@ module.exports = {
309
369
  }));
310
370
  }
311
371
 
312
- function getIcons() {
372
+ function writeImportFile ({
373
+ importFile,
374
+ prologue,
375
+ icon,
376
+ components,
377
+ tiptap,
378
+ app,
379
+ indexJs,
380
+ indexSass
381
+ }) {
382
+ fs.writeFileSync(importFile, (prologue || '') + stripIndent`
383
+ ${(icon && icon.importCode) || ''}
384
+ ${(icon && icon.registerCode) || ''}
385
+ ${(components && components.importCode) || ''}
386
+ ${(tiptap && tiptap.importCode) || ''}
387
+ ${(app && app.importCode) || ''}
388
+ ${(indexJs && indexJs.importCode) || ''}
389
+ ${(indexSass && indexSass.importCode) || ''}
390
+ ${(icon && icon.registerCode) || ''}
391
+ ${(components && components.registerCode) || ''}
392
+ ${(tiptap && tiptap.registerCode) || ''}
393
+ ` +
394
+ (app ? stripIndent`
395
+ setTimeout(() => {
396
+ ${app.invokeCode}
397
+ }, 0);
398
+ ` : '') +
399
+ // No delay on these, they expect to run early like ui/public code
400
+ // and the first ones invoked set up expected stuff like apos.http
401
+ (indexJs ? stripIndent`
402
+ ${indexJs.invokeCode}
403
+ ` : '')
404
+ );
405
+ }
313
406
 
314
- for (const name of self.apos.modulesToBeInstantiated()) {
407
+ function getIcons() {
408
+ for (const name of modulesToInstantiate) {
315
409
  const metadata = self.apos.synth.getMetadata(name);
316
410
  // icons is an unparsed section, so getMetadata gives it back
317
411
  // to us as an object with a property for each class in the
@@ -347,45 +441,65 @@ module.exports = {
347
441
  return output;
348
442
  }
349
443
 
350
- function merge(scene) {
351
- const jsModules = `${scene}-module-bundle.js`;
352
- const jsNoModules = `${scene}-nomodule-bundle.js`;
353
- const css = `${scene}-bundle.css`;
354
- fs.writeFileSync(
355
- `${bundleDir}/${jsModules}`,
356
- Object.entries(self.builds).filter(
357
- ([ name, options ]) => options.scenes.includes(scene) &&
358
- options.outputs.includes('js') &&
359
- (!options.condition || options.condition === 'module')
360
- ).map(([ name, options ]) => {
361
- return `// BUILD: ${name}\n` + fs.readFileSync(`${bundleDir}/${name}-build.js`, 'utf8');
362
- }).join('\n')
363
- );
364
- fs.writeFileSync(
365
- `${bundleDir}/${jsNoModules}`,
366
- Object.entries(self.builds).filter(
367
- ([ name, options ]) => options.scenes.includes(scene) &&
368
- options.outputs.includes('js') &&
369
- (!options.condition || options.condition === 'nomodule')
370
- ).map(([ name, options ]) => {
371
- return `// BUILD: ${name}\n` + fs.readFileSync(`${bundleDir}/${name}-build.js`, 'utf8');
372
- }).join('\n')
373
- );
374
- fs.writeFileSync(
375
- `${bundleDir}/${css}`,
376
- Object.entries(self.builds).filter(
377
- ([ name, options ]) => options.scenes.includes(scene) &&
378
- options.outputs.includes('css')
379
- ).map(([ name, options ]) => {
380
- const file = `${bundleDir}/${name}-build.css`;
381
- if (fs.existsSync(file)) {
382
- return `/* BUILD: ${name} */\n` + fs.readFileSync(file, 'utf8');
383
- } else {
384
- return '';
444
+ function merge(scenes) {
445
+ return scenes.reduce((acc, scene) => {
446
+ const jsModules = `${scene}-module-bundle.js`;
447
+ const jsNoModules = `${scene}-nomodule-bundle.js`;
448
+ const css = `${scene}-bundle.css`;
449
+
450
+ writeSceneBundle({
451
+ scene,
452
+ filePath: jsModules,
453
+ jsCondition: 'module'
454
+ });
455
+ writeSceneBundle({
456
+ scene,
457
+ filePath: jsNoModules,
458
+ jsCondition: 'nomodule'
459
+ });
460
+ writeSceneBundle({
461
+ scene,
462
+ filePath: css,
463
+ checkForFile: true
464
+ });
465
+
466
+ return [
467
+ ...acc,
468
+ jsModules,
469
+ jsNoModules,
470
+ css
471
+ ];
472
+ }, []);
473
+ }
474
+
475
+ function writeSceneBundle ({
476
+ scene, filePath, jsCondition, checkForFile = false
477
+ }) {
478
+ const [ _ext, fileExt ] = filePath.match(/\.(\w+)$/);
479
+ const filterBuilds = ({
480
+ scenes, outputs, condition
481
+ }) => {
482
+ return outputs.includes(fileExt) &&
483
+ ((!condition || !jsCondition) || condition === jsCondition) &&
484
+ scenes.includes(scene);
485
+ };
486
+
487
+ const filesContent = Object.entries(self.builds)
488
+ .filter(([ _, options ]) => filterBuilds(options))
489
+ .map(([ name ]) => {
490
+ const file = `${bundleDir}/${name}-build.${fileExt}`;
491
+ const readFile = (n, f) => `/* BUILD: ${n} */\n${fs.readFileSync(f, 'utf8')}`;
492
+
493
+ if (checkForFile) {
494
+ return fs.existsSync(file)
495
+ ? readFile(name, file)
496
+ : '';
385
497
  }
386
- }).join('\n')
387
- );
388
- return [ jsModules, jsNoModules, css ];
498
+
499
+ return readFile(name, file);
500
+ }).join('\n');
501
+
502
+ fs.writeFileSync(`${bundleDir}/${filePath}`, filesContent);
389
503
  }
390
504
 
391
505
  // If NODE_ENV is production, this function will copy the given
@@ -432,10 +546,10 @@ module.exports = {
432
546
  return fs.copyFile(from, to);
433
547
  }
434
548
 
435
- function getImports(folder, pattern, options) {
549
+ function getImports(folder, pattern, options = {}) {
436
550
  let components = [];
437
551
  const seen = {};
438
- for (const name of self.apos.modulesToBeInstantiated()) {
552
+ for (const name of modulesToInstantiate) {
439
553
  const metadata = self.apos.synth.getMetadata(name);
440
554
  for (const entry of metadata.__meta.chain) {
441
555
  if (seen[entry.dirname]) {
@@ -445,12 +559,6 @@ module.exports = {
445
559
  seen[entry.dirname] = true;
446
560
  }
447
561
  }
448
- const output = {
449
- importCode: '',
450
- registerCode: '',
451
- invokeCode: '',
452
- paths: []
453
- };
454
562
 
455
563
  if (options.importLastVersion) {
456
564
  // Reverse the list so we can easily find the last configured import
@@ -471,6 +579,17 @@ module.exports = {
471
579
  components.reverse();
472
580
  }
473
581
 
582
+ return getImportFileOutput(components, options);
583
+ }
584
+
585
+ function getImportFileOutput (components, options = {}) {
586
+ const output = {
587
+ importCode: '',
588
+ registerCode: '',
589
+ invokeCode: '',
590
+ paths: []
591
+ };
592
+
474
593
  components.forEach((component, i) => {
475
594
  if (options.requireDefaultExport) {
476
595
  if (!fs.readFileSync(component, 'utf8').match(/export[\s\n]+default/)) {
@@ -485,10 +604,11 @@ module.exports = {
485
604
  const jsFilename = JSON.stringify(component);
486
605
  const name = getComponentName(component, options, i);
487
606
  const jsName = JSON.stringify(name);
488
- output.paths.push(component);
489
607
  const importCode = `
490
608
  import ${name}${options.importSuffix || ''} from ${jsFilename};
491
609
  `;
610
+
611
+ output.paths.push(component);
492
612
  output.importCode += `${importCode}\n`;
493
613
 
494
614
  if (options.registerComponents) {
@@ -505,6 +625,7 @@ module.exports = {
505
625
  output.invokeCode += `${name}${options.importSuffix || ''}();\n`;
506
626
  }
507
627
  });
628
+
508
629
  return output;
509
630
  }
510
631
 
@@ -523,8 +644,11 @@ module.exports = {
523
644
  return pkgTimestamp > parseInt(timestamp);
524
645
  }
525
646
 
526
- function getComponentName(component, options, i) {
527
- return require('path').basename(component).replace(/\.\w+/, '') + (options.enumerateImports ? `_${i}` : '');
647
+ function getComponentName(component, { enumerateImports } = {}, i) {
648
+ return path
649
+ .basename(component)
650
+ .replace(/-/g, '_')
651
+ .replace(/\.\w+/, '') + (enumerateImports ? `_${i}` : '');
528
652
  }
529
653
 
530
654
  function cleanErrors(errors) {
@@ -555,22 +679,10 @@ module.exports = {
555
679
  }
556
680
  },
557
681
  stylesheetsHelper(when) {
558
- const base = self.getAssetBaseUrl();
559
- const bundle = `<link href="${base}/${when}-bundle.css" rel="stylesheet" />`;
560
- return self.apos.template.safe(bundle);
682
+ return '';
561
683
  },
562
684
  scriptsHelper(when) {
563
- const base = self.getAssetBaseUrl();
564
- if (self.options.es5) {
565
- return self.apos.template.safe(stripIndent`
566
- <script nomodule src="${base}/${when}-nomodule-bundle.js"></script>
567
- <script type="module" src="${base}/${when}-module-bundle.js"></script>
568
- `);
569
- } else {
570
- return self.apos.template.safe(stripIndent`
571
- <script src="${base}/${when}-module-bundle.js"></script>
572
- `);
573
- }
685
+ return '';
574
686
  },
575
687
  shouldRefreshOnRestart() {
576
688
  return self.options.refreshOnRestart && (process.env.NODE_ENV !== 'production');
@@ -47,11 +47,11 @@ module.exports = ({
47
47
  Modules: path.resolve(modulesDir)
48
48
  },
49
49
  modules: [
50
- // TODO change this if we decide to namespace the
51
- // apostrophe module itself
50
+ 'node_modules',
52
51
  `${apos.npmRootDir}/node_modules/apostrophe/node_modules`,
53
52
  `${apos.npmRootDir}/node_modules`
54
- ]
53
+ ],
54
+ symlinks: false
55
55
  },
56
56
  stats: 'verbose',
57
57
  plugins: process.env.APOS_BUNDLE_ANALYZER ? [ new BundleAnalyzerPlugin() ] : []