@abcnews/aunty 14.0.0 → 14.0.2

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.
package/README.md CHANGED
@@ -120,7 +120,7 @@ If you don't need to override any of the project defaults, your entire aunty con
120
120
  | `hasBundleAnalysis` | `false` | Setting this to true will spin up a second server on another port, allowing you to inspect your bundle. The address will be logged to the console when the dev server starts. |
121
121
  | `host` | `"localhost"` | The hostname of your dev server. If you're on the ABC internal network, the default will change to your machine's hostname (`ws<number>.aus.aunty.abc.net.au`). The `AUNTY_HOST` environment variable, if present, will override this setting. |
122
122
  | `hot` | `true` | Should the dev server enable hot reloading. If `NODE_ENV !== "development"`, the default will change to `false`. |
123
- | `https` | `true` | Should the dev server use SSL (with a self-signed certificate matching the `host`). You can alternatively supply your own `{cert: string, key: string` object if you've generated your certificate some other way. |
123
+ | `https` | `true` | Should the dev server use SSL (with a self-signed certificate matching the `host`). You can alternatively supply your own `{cert: string, key: string}` object if you've generated your certificate some other way. |
124
124
  | `port` | `8000` | The port number of your dev server. If the port specified is unavailable, **aunty** will try incrementing the port number until it finds an available one. The `AUNTY_PORT` environment variable, if present, will override this setting. |
125
125
 
126
126
  #### `deploy` config properties
@@ -156,7 +156,7 @@ Note: You may also need a `Promise` polyfill for IE11.
156
156
 
157
157
  ## Authors
158
158
 
159
- - Colin Gourlay ([gourlay.colin@abc.net.au](mailto:gourlay.colin@abc.net.au))
159
+ - Colin Gourlay
160
160
  - Simon Elvery ([elvery.simon@abc.net.au](mailto:elvery.simon@abc.net.au))
161
161
  - Joshua Byrd ([byrd.joshua@abc.net.au](mailto:byrd.joshua@abc.net.au))
162
162
  - Nathan Hoad
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abcnews/aunty",
3
- "version": "14.0.0",
3
+ "version": "14.0.2",
4
4
  "description": "A toolkit for working with ABC News projects",
5
5
  "repository": "abcnews/aunty",
6
6
  "license": "MIT",
@@ -12,7 +12,8 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "postinstall": "node scripts/postinstall.js",
15
- "release": "release-it"
15
+ "release": "release-it",
16
+ "test": "jest test/test.js"
16
17
  },
17
18
  "files": [
18
19
  "assets",
@@ -94,6 +95,7 @@
94
95
  "babel-eslint": "^10.1.0",
95
96
  "eslint": "^7.7.0",
96
97
  "eslint-config-prettier": "^8.2.0",
97
- "release-it": "^15.1.4"
98
+ "release-it": "^15.1.4",
99
+ "yeoman-automation-adapter": "^2.0.0"
98
100
  }
99
101
  }
@@ -19,77 +19,82 @@ const { command } = require('../');
19
19
  const cleanCommand = require('../clean');
20
20
  const { MESSAGES, OPTIONS } = require('./constants');
21
21
 
22
- module.exports = command(
23
- {
24
- name: 'build',
25
- nodeEnv: 'production',
26
- options: OPTIONS,
27
- usage: MESSAGES.usage
28
- },
29
- async argv => {
30
- const { root } = getProjectConfig();
31
- const webpackConfig = getWebpackConfig();
32
- let deployConfig;
33
-
34
- if (!argv.local) {
35
- deployConfig = getDeployConfig({ id: argv.id });
36
- webpackConfig.forEach(config => {
37
- config.output.publicPath = deployConfig.targets[0].publicPath;
38
- });
39
- }
40
-
41
- if (argv.dry) {
42
- return dry(
43
- combine(
44
- {
45
- 'Webpack config': webpackConfig
46
- },
47
- deployConfig
48
- ? {
49
- 'Deploy config': deployConfig
50
- }
51
- : {}
52
- )
53
- );
54
- }
22
+ const build = async argv => {
23
+ const { root } = getProjectConfig();
24
+ const webpackConfig = getWebpackConfig();
25
+ let deployConfig;
55
26
 
56
- throws(await cleanCommand(['--quiet']));
27
+ if (!argv.local) {
28
+ deployConfig = getDeployConfig({ id: argv.id });
29
+ webpackConfig.forEach(config => {
30
+ config.output.publicPath = deployConfig.targets[0].publicPath;
31
+ });
32
+ }
57
33
 
58
- info(
59
- MESSAGES.build({
60
- id: deployConfig ? deployConfig.id : null,
61
- publicPaths: deployConfig ? deployConfig.targets.map(x => x.publicPath) : [webpackConfig[0].output.publicPath]
62
- })
34
+ if (argv.dry) {
35
+ return dry(
36
+ combine(
37
+ {
38
+ 'Webpack config': webpackConfig
39
+ },
40
+ deployConfig
41
+ ? {
42
+ 'Deploy config': deployConfig
43
+ }
44
+ : {}
45
+ )
63
46
  );
47
+ }
64
48
 
65
- let spinner = spin('Building');
66
- const startTime = Date.now();
67
- const compiler = webpack(webpackConfig);
68
- const stats = unpack(await packs(pify(compiler.run.bind(compiler)))());
49
+ throws(await cleanCommand(['--quiet']));
69
50
 
70
- if (stats.hasErrors()) {
71
- const errors = stats.toJson({}, true).errors;
51
+ info(
52
+ MESSAGES.build({
53
+ id: deployConfig ? deployConfig.id : null,
54
+ publicPaths: deployConfig ? deployConfig.targets.map(x => x.publicPath) : [webpackConfig[0].output.publicPath]
55
+ })
56
+ );
72
57
 
73
- spinner.fail();
58
+ let spinner = spin('Building');
59
+ const startTime = Date.now();
60
+ const compiler = webpack(webpackConfig);
61
+ const stats = unpack(await packs(pify(compiler.run.bind(compiler)))());
74
62
 
75
- if (errors.length > 1) {
76
- throw MESSAGES.multipleErrors(errors.map(error => error.message));
77
- }
63
+ if (stats.hasErrors()) {
64
+ const errors = stats.toJson({}, true).errors;
78
65
 
79
- throw errors[0];
80
- }
66
+ spinner.fail();
81
67
 
82
- if (stats.hasWarnings()) {
83
- spinner.warn();
84
- stats.toJson({}, true).warnings.forEach(warning => warn(warning.message));
85
- } else {
86
- spinner.succeed(`Built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`);
68
+ if (errors.length > 1) {
69
+ throw MESSAGES.multipleErrors(errors.map(error => error.message));
87
70
  }
88
71
 
89
- if (deployConfig) {
90
- spinner = spin('Creating deploy configuration');
91
- writeJsonFile.sync(join(root, OUTPUT_DIRECTORY_NAME, DEPLOY_FILE_NAME), deployConfig);
92
- spinner.succeed('Created deploy configuration');
93
- }
72
+ throw errors[0];
94
73
  }
74
+
75
+ if (stats.hasWarnings()) {
76
+ spinner.warn();
77
+ stats.toJson({}, true).warnings.forEach(warning => warn(warning.message));
78
+ } else {
79
+ spinner.succeed(`Built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`);
80
+ }
81
+
82
+ if (deployConfig) {
83
+ spinner = spin('Creating deploy configuration');
84
+ writeJsonFile.sync(join(root, OUTPUT_DIRECTORY_NAME, DEPLOY_FILE_NAME), deployConfig);
85
+ spinner.succeed('Created deploy configuration');
86
+ }
87
+ };
88
+
89
+ module.exports = command(
90
+ {
91
+ name: 'build',
92
+ nodeEnv: 'production',
93
+ options: OPTIONS,
94
+ usage: MESSAGES.usage
95
+ },
96
+ build
95
97
  );
98
+
99
+ // Expose this for the tests
100
+ module.exports._testBuild = build;
@@ -1,7 +1,6 @@
1
1
  // Ours
2
2
  const { createLogo } = require('../utils/branding');
3
3
  const { cmd, dim, hvy, opt, req, sec } = require('../utils/color');
4
- const { setOfValues } = require('../utils/structures');
5
4
 
6
5
  const COMMAND_ALIASES = (module.exports.COMMAND_ALIASES = {
7
6
  b: 'build',
@@ -14,7 +13,7 @@ const COMMAND_ALIASES = (module.exports.COMMAND_ALIASES = {
14
13
  t: 'test'
15
14
  });
16
15
 
17
- module.exports.COMMANDS = setOfValues(COMMAND_ALIASES);
16
+ module.exports.COMMANDS = new Set(Object.values(COMMAND_ALIASES));
18
17
 
19
18
  module.exports.DEFAULTS = {
20
19
  name: '__command__',
@@ -1,6 +1,5 @@
1
1
  // Ours
2
2
  const { cmd, hvy, opt, req, sec } = require('../../utils/color');
3
- const { setOfValues } = require('../../utils/structures');
4
3
 
5
4
  module.exports.OPTIONS = {
6
5
  boolean: ['announce'],
@@ -14,7 +13,7 @@ const GENERATOR_ALIASES = (module.exports.GENERATOR_ALIASES = {
14
13
  p: 'project'
15
14
  });
16
15
 
17
- module.exports.GENERATORS = setOfValues(GENERATOR_ALIASES);
16
+ module.exports.GENERATORS = new Set(Object.values(GENERATOR_ALIASES));
18
17
 
19
18
  module.exports.MESSAGES = {
20
19
  generatorDoesNotExist: name => `The generator '${name}' does not exist.`,
@@ -10,55 +10,63 @@ const { log } = require('../../utils/logging');
10
10
  const { command } = require('../');
11
11
  const { GENERATOR_ALIASES, GENERATORS, MESSAGES, OPTIONS } = require('./constants');
12
12
 
13
- module.exports = command(
14
- {
15
- name: 'generate',
16
- hasSubcommands: true,
17
- options: OPTIONS
18
- },
19
- async argv => {
20
- if (argv.dry) {
21
- throw MESSAGES.noDryRuns;
22
- }
23
-
24
- const generatorName = GENERATOR_ALIASES[argv._[0]] || argv._[0];
13
+ const generate = async argv => {
14
+ if (argv.dry) {
15
+ throw MESSAGES.noDryRuns;
16
+ }
25
17
 
26
- // If we didn't supply a known generator name, blow up, unless we wanted
27
- // the generate command's usage message (in which case, print it, then exit).
18
+ const generatorName = GENERATOR_ALIASES[argv._[0]] || argv._[0];
28
19
 
29
- if (!GENERATORS.has(generatorName)) {
30
- if (!generatorName || generatorName.indexOf('-') === 0 || argv.help) {
31
- return log(MESSAGES.usage);
32
- }
20
+ // If we didn't supply a known generator name, blow up, unless we wanted
21
+ // the generate command's usage message (in which case, print it, then exit).
33
22
 
34
- throw MESSAGES.generatorDoesNotExist(generatorName);
23
+ if (!GENERATORS.has(generatorName)) {
24
+ if (!generatorName || generatorName.indexOf('-') === 0 || argv.help) {
25
+ return log(MESSAGES.usage);
35
26
  }
36
27
 
37
- log(createCommandLogo(`generate ${generatorName}`));
28
+ throw MESSAGES.generatorDoesNotExist(generatorName);
29
+ }
38
30
 
39
- const env = yeoman.createEnv();
31
+ log(createCommandLogo(`generate ${generatorName}`));
40
32
 
41
- // Register the generator
33
+ // If we're in a test environment, insert the auto adapter with the provided answers
34
+ const env = global.auntyYeomanAnswers
35
+ ? yeoman.createEnv([], {}, new (require('yeoman-automation-adapter').AutoAdapter)(global.auntyYeomanAnswers, true))
36
+ : yeoman.createEnv();
42
37
 
43
- const generatorPath = require.resolve(`../../generators/${generatorName}`);
38
+ // Register the generator
44
39
 
45
- env.register(generatorPath, generatorName);
40
+ const generatorPath = require.resolve(`../../generators/${generatorName}`);
46
41
 
47
- // Run the generator, including known arguments
42
+ env.register(generatorPath, generatorName);
48
43
 
49
- const runArgs = [generatorName]
50
- .concat(argv['--'])
51
- .concat(argv.help ? ['--help'] : [])
52
- .join(' ');
44
+ // Run the generator, including known arguments
53
45
 
54
- const [err] = await pack(env.run(runArgs));
46
+ const runArgs = [generatorName]
47
+ .concat(argv['--'])
48
+ .concat(argv.help ? ['--help'] : [])
49
+ .join(' ');
55
50
 
56
- if (err) {
57
- throw err;
58
- }
51
+ const [err] = await pack(env.run(runArgs));
59
52
 
60
- if (argv.announce) {
61
- announce();
62
- }
53
+ if (err) {
54
+ throw err;
55
+ }
56
+
57
+ if (argv.announce) {
58
+ announce();
63
59
  }
60
+ };
61
+
62
+ module.exports = command(
63
+ {
64
+ name: 'generate',
65
+ hasSubcommands: true,
66
+ options: OPTIONS
67
+ },
68
+ generate
64
69
  );
70
+
71
+ // Expose this for the tests
72
+ module.exports._testGenerate = generate;
@@ -8,7 +8,7 @@ module.exports.BUNDLE_ANALYZER_CONFIG = {
8
8
  };
9
9
 
10
10
  module.exports.MESSAGES = {
11
- port: port => `Port ${port} already in use attempting to use port ${port + 1}`,
11
+ port: ({ port, errorType }) => `Port ${port} could not be used (${errorType}). Attempting to use port ${port + 1}`,
12
12
  analysis: ({ analyzerHost, analyzerPort }) => `http://${analyzerHost}:${analyzerPort}`,
13
13
  serve: ({ bundleAnalysisPath, hot, publicPath }) => `Serve (${hvy(process.env.NODE_ENV)}):${
14
14
  bundleAnalysisPath
@@ -9,7 +9,7 @@ const mem = require('mem');
9
9
 
10
10
  // Ours
11
11
  const { hvy } = require('../utils/color');
12
- const { pretty, warn } = require('../utils/logging');
12
+ const { pretty } = require('../utils/logging');
13
13
  const { combine } = require('../utils/structures');
14
14
  const { PROJECT_CONFIG_FILE_NAME } = require('../constants');
15
15
 
@@ -12,7 +12,7 @@ const { combine } = require('../utils/structures');
12
12
  const { getProjectConfig } = require('./project');
13
13
  const { info } = require('../utils/logging');
14
14
  const { MESSAGES } = require('../cli/serve/constants');
15
- const {INTERNAL_TEST_HOST} = require('../constants')
15
+ const { INTERNAL_TEST_HOST } = require('../constants');
16
16
 
17
17
  const HOME_DIR = homedir();
18
18
  const SSL_DIR = '.aunty/ssl';
@@ -38,10 +38,10 @@ const addEnvironmentVariables = config => {
38
38
 
39
39
  const getSSLPath = (module.exports.getSSLPath = (host, name) => join(HOME_DIR, SSL_DIR, host, name || '.'));
40
40
 
41
- /*
42
- Set config.https to cert & key generated with `aunty sign-cert` (if they both exist)
43
- We expect them to be in: ~/.aunty/ssl/<host>/server.{cert|key}
44
- */
41
+ /**
42
+ * Set config.https to cert & key generated with `aunty sign-cert` (if they both exist)
43
+ * We expect them to be in: ~/.aunty/ssl/<host>/server.{cert|key}
44
+ */
45
45
  const addUserSSLConfig = config => {
46
46
  if (config.https === true) {
47
47
  try {
@@ -55,13 +55,16 @@ const addUserSSLConfig = config => {
55
55
  return config;
56
56
  };
57
57
 
58
+ /**
59
+ * Find an open port, or keep incrementing until we get one
60
+ */
58
61
  const findPort = async (port, max = port + 100, host = '0.0.0.0') => {
59
62
  return new Promise((resolve, reject) => {
60
63
  const socket = new Socket();
61
64
 
62
- const next = () => {
65
+ const next = errorType => {
63
66
  socket.destroy();
64
- info(MESSAGES.port(port));
67
+ info(MESSAGES.port({ port, errorType }));
65
68
  if (port <= max) resolve(findPort(port + 1, max, host));
66
69
  else reject(new Error('Could not find an available port'));
67
70
  };
@@ -72,7 +75,7 @@ const findPort = async (port, max = port + 100, host = '0.0.0.0') => {
72
75
  };
73
76
 
74
77
  // Port is taken if connection can be made
75
- socket.once('connect', next);
78
+ socket.once('connect', () => next('in use'));
76
79
 
77
80
  // Port is open if connection attempt times out
78
81
  socket.setTimeout(500);
@@ -83,9 +86,19 @@ const findPort = async (port, max = port + 100, host = '0.0.0.0') => {
83
86
  // If the connection is refused, it's assumed nothing is listening and the port is available.
84
87
  if (e.code === 'ECONNREFUSED') {
85
88
  found();
89
+ } else if (e.code === 'ENOTFOUND' && e.syscall === 'getaddrinfo' && e.hostname?.includes(INTERNAL_SUFFIX)) {
90
+ console.error(
91
+ [
92
+ 'Could not resolve hostname ' + e.hostname,
93
+ 'You appear to be on the ABC network without a hostname attached to your computer.',
94
+ "Aunty can't continue. Consider reconnecting, or add your hostname to your hosts file.",
95
+ ''
96
+ ].join('\n')
97
+ );
98
+ process.exit(1);
86
99
  } else {
87
- // Not sure what to do with other errors, so keep seeking a free port.
88
- next();
100
+ // Not sure what to do with other errors. Log the code & keep seeking a free port.
101
+ next(`error code ${e.code}`);
89
102
  }
90
103
  });
91
104
 
@@ -18,6 +18,10 @@ const { getBuildConfig } = require('./build');
18
18
  const { getProjectConfig } = require('./project');
19
19
 
20
20
  const JSX_RESOLVE_EXTENSIONS = ['.jsx', '.tsx'];
21
+
22
+ /**
23
+ * Project types to override the Webpack config.
24
+ */
21
25
  const PROJECT_TYPES_CONFIG = {
22
26
  preact: {
23
27
  resolve: {
@@ -34,6 +38,11 @@ const PROJECT_TYPES_CONFIG = {
34
38
  extensions: JSX_RESOLVE_EXTENSIONS
35
39
  }
36
40
  },
41
+
42
+ /**
43
+ * Svelte uses a function to modify the existing config, rather than just merging in.
44
+ * @see combine
45
+ */
37
46
  svelte: config => {
38
47
  config.resolve = {
39
48
  // Make sure that only one copy of the Svelte runtime is bundled in the app
@@ -44,7 +53,8 @@ const PROJECT_TYPES_CONFIG = {
44
53
  extensions: [...config.resolve.extensions, '.svelte'],
45
54
  // When using Svelte components installed from npm, use the original component
46
55
  // source code, rather than consuming the already-compiled version
47
- mainFields: ['svelte', 'browser', 'module', 'main']
56
+ mainFields: ['svelte', 'browser', 'module', 'main'],
57
+ conditionNames: ['svelte', 'browser', 'import']
48
58
  };
49
59
 
50
60
  const { include, loader, options } = getHintedRule(config, 'scripts');
@@ -167,6 +177,10 @@ function createWebpackConfig({ isModernJS } = {}) {
167
177
  module: {
168
178
  rules: [
169
179
  {
180
+ /**
181
+ * hints are used by PROJECT_TYPES_CONFIGs to quickly select the right config.
182
+ * @see PROJECT_TYPES_CONFIG
183
+ */
170
184
  __hint__: 'scripts',
171
185
  test: hasTS ? /\.m?[jt]sx?$/ : /\.m?jsx?$/,
172
186
  include: [resolve(root, from)].concat(resolveIncludedDependencies(includedDependencies, root)),
@@ -26,7 +26,7 @@ if (module.hot) {
26
26
  module.hot.accept('./components/App', () => {
27
27
  try {
28
28
  renderApp();
29
- } catch (err) {
29
+ } catch (err<% if (isTS) { %>: any<% } %>) {
30
30
  import('./components/ErrorBox').then(({ default: ErrorBox }) => {
31
31
  if (appMountEl) {
32
32
  render(<ErrorBox error={err} />, appMountEl);
@@ -27,7 +27,7 @@ if (module.hot) {
27
27
  module.hot.accept('./components/App', () => {
28
28
  try {
29
29
  renderApp();
30
- } catch (err: any) {
30
+ } catch (err<% if (isTS) { %>: any<% } %>) {
31
31
  import('./components/ErrorBox').then(({ default: ErrorBox }) => {
32
32
  root.render(<ErrorBox error={err} />);
33
33
  });
@@ -1,5 +1,12 @@
1
1
  const deepmerge = require('deepmerge');
2
2
 
3
+ /**
4
+ * Merge a function or object into the target object
5
+ * @param target an object to merge into
6
+ * @param source an object to merge with `target`, or a function that takes `target` as an argument
7
+ * @param isDeep use deepmerge to merge
8
+ * @returns
9
+ */
3
10
  const combine = (target, source, isDeep) =>
4
11
  typeof source === 'function'
5
12
  ? source(target)
@@ -11,11 +18,16 @@ const combine = (target, source, isDeep) =>
11
18
 
12
19
  const combineDeep = (memo, source) => combine(memo, source, true);
13
20
 
21
+ /**
22
+ * Shallow merge an array of objects. Earlier values overwritten by later values
23
+ * @param sources Array of objects and functions to be merged using `combine`
24
+ * @see combine
25
+ */
14
26
  module.exports.combine = (...sources) => sources.reduce(combine, {});
15
27
 
28
+ /**
29
+ * Deep merge an array of objects. Earlier values overwritten by later values.
30
+ * @param sources Array of objects and functions to be merged using `combine`
31
+ * @see combine
32
+ */
16
33
  module.exports.merge = (...sources) => sources.reduce(combineDeep, {});
17
-
18
- module.exports.setOfKeysAndValues = source =>
19
- new Set([].concat(Object.keys(source).map(key => source[key])).concat(Object.keys(source)));
20
-
21
- module.exports.setOfValues = source => new Set(Object.keys(source).map(key => source[key]));