@stencil/core 2.15.1 → 2.16.1-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 (50) hide show
  1. package/cli/index.cjs +115 -27
  2. package/cli/index.js +115 -27
  3. package/cli/package.json +1 -1
  4. package/compiler/package.json +1 -1
  5. package/compiler/stencil.js +821 -291
  6. package/compiler/stencil.min.js +2 -2
  7. package/dependencies.json +1 -1
  8. package/dev-server/client/index.js +1 -1
  9. package/dev-server/client/package.json +1 -1
  10. package/dev-server/client/test/hmr-util.spec.d.ts +1 -0
  11. package/dev-server/client/test/status.spec.d.ts +1 -0
  12. package/dev-server/connector.html +3 -3
  13. package/dev-server/index.js +1 -1
  14. package/dev-server/package.json +1 -1
  15. package/dev-server/server-process.js +5 -3
  16. package/internal/app-data/package.json +1 -1
  17. package/internal/client/css-shim.js +1 -1
  18. package/internal/client/dom.js +1 -1
  19. package/internal/client/index.js +11 -9
  20. package/internal/client/package.json +1 -1
  21. package/internal/client/patch-browser.js +1 -1
  22. package/internal/client/patch-esm.js +1 -1
  23. package/internal/client/shadow-css.js +2 -3
  24. package/internal/hydrate/index.js +37 -32
  25. package/internal/hydrate/package.json +1 -1
  26. package/internal/hydrate/shadow-css.js +9 -9
  27. package/internal/package.json +1 -1
  28. package/internal/stencil-private.d.ts +39 -3
  29. package/internal/stencil-public-compiler.d.ts +70 -3
  30. package/internal/stencil-public-docs.d.ts +3 -0
  31. package/internal/testing/index.js +35 -30
  32. package/internal/testing/package.json +1 -1
  33. package/internal/testing/shadow-css.js +9 -9
  34. package/mock-doc/index.cjs +8 -2
  35. package/mock-doc/index.d.ts +9 -2
  36. package/mock-doc/index.js +8 -2
  37. package/mock-doc/package.json +1 -1
  38. package/package.json +22 -16
  39. package/screenshot/index.js +2 -0
  40. package/screenshot/package.json +1 -1
  41. package/sys/node/autoprefixer.js +1 -1
  42. package/sys/node/index.js +2038 -1330
  43. package/sys/node/package.json +1 -1
  44. package/sys/node/worker.js +1 -1
  45. package/testing/index.js +335 -331
  46. package/testing/jest/test/jest-config.spec.d.ts +1 -0
  47. package/testing/jest/test/jest-preprocessor.spec.d.ts +1 -0
  48. package/testing/jest/test/jest-runner.spec.d.ts +1 -0
  49. package/testing/jest/test/jest-serializer.spec.d.ts +1 -0
  50. package/testing/package.json +1 -1
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Stencil CLI (CommonJS) v2.15.1 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI (CommonJS) v2.16.1-0 | MIT Licensed | https://stenciljs.com
3
3
  */
4
4
  'use strict';
5
5
 
@@ -118,6 +118,8 @@ const TASK_CANCELED_MSG = `task canceled`;
118
118
  * Forward-slash paths can be used in Windows as long as they're not
119
119
  * extended-length paths and don't contain any non-ascii characters.
120
120
  * This was created since the path methods in Node.js outputs \\ paths on Windows.
121
+ * @param path the Windows-based path to convert
122
+ * @returns the converted path
121
123
  */
122
124
  const normalizePath = (path) => {
123
125
  if (typeof path !== 'string') {
@@ -530,7 +532,7 @@ const getNpmConfigEnvArgs = (sys) => {
530
532
  const dependencies = [
531
533
  {
532
534
  name: "@stencil/core",
533
- version: "2.15.1",
535
+ version: "2.16.1-0",
534
536
  main: "compiler/stencil.js",
535
537
  resources: [
536
538
  "package.json",
@@ -967,10 +969,10 @@ async function updateConfig(sys, newOptions) {
967
969
 
968
970
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
969
971
  const DOCS_CUSTOM = 'docs-custom';
970
- const DOCS_JSON = `docs-json`;
971
- const DOCS_README = `docs-readme`;
972
- const DOCS_VSCODE = `docs-vscode`;
973
- const WWW = `www`;
972
+ const DOCS_JSON = 'docs-json';
973
+ const DOCS_README = 'docs-readme';
974
+ const DOCS_VSCODE = 'docs-vscode';
975
+ const WWW = 'www';
974
976
 
975
977
  /**
976
978
  * Used to within taskBuild to provide the component_count property.
@@ -1072,10 +1074,12 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1072
1074
  };
1073
1075
  };
1074
1076
  /**
1075
- * Reads package-lock.json, yarn.lock, and package.json files in order to cross reference
1077
+ * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1076
1078
  * the dependencies and devDependencies properties. Pulls up the current installed version
1077
1079
  * of each package under the @stencil, @ionic, and @capacitor scopes.
1078
- * @returns string[]
1080
+ * @param sys the system instance where telemetry is invoked
1081
+ * @param config the Stencil configuration associated with the current task that triggered telemetry
1082
+ * @returns an object listing all dev and production dependencies under the aforementioned scopes
1079
1083
  */
1080
1084
  async function getInstalledPackages(sys, config) {
1081
1085
  let packages = [];
@@ -1154,7 +1158,12 @@ function sanitizeDeclaredVersion(version) {
1154
1158
  return version.replace(/[*^~]/g, '');
1155
1159
  }
1156
1160
  /**
1157
- * If telemetry is enabled, send a metric via IPC to a forked process for uploading.
1161
+ * If telemetry is enabled, send a metric to an external data store
1162
+ * @param sys the system instance where telemetry is invoked
1163
+ * @param config the Stencil configuration associated with the current task that triggered telemetry
1164
+ * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
1165
+ * "Stencil Version". For example, "stencil_cli_command" is a name that is used to track all CLI command information.
1166
+ * @param value the data to send to the external data store under the provided name argument
1158
1167
  */
1159
1168
  async function sendMetric(sys, config, name, value) {
1160
1169
  const session_id = await getTelemetryToken(sys);
@@ -1307,7 +1316,14 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1307
1316
  (!global.origin || typeof global.origin !== 'string');
1308
1317
 
1309
1318
  /**
1310
- * Task to generate component boilerplate.
1319
+ * Task to generate component boilerplate and write it to disk. This task can
1320
+ * cause the program to exit with an error under various circumstances, such as
1321
+ * being called in an inappropriate place, being asked to overwrite files that
1322
+ * already exist, etc.
1323
+ *
1324
+ * @param coreCompiler the CoreCompiler we're using currently, here we're
1325
+ * mainly accessing the `path` module
1326
+ * @param config the user-supplied config, which we need here to access `.sys`.
1311
1327
  */
1312
1328
  const taskGenerate = async (coreCompiler, config) => {
1313
1329
  if (!IS_NODE_ENV) {
@@ -1337,10 +1353,16 @@ const taskGenerate = async (coreCompiler, config) => {
1337
1353
  const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
1338
1354
  const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
1339
1355
  await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
1340
- const writtenFiles = await Promise.all(extensionsToGenerate.map((extension) => writeFileByExtension(coreCompiler, config, outDir, componentName, extension, extensionsToGenerate.includes('css')))).catch((error) => config.logger.error(error));
1356
+ const filesToGenerate = extensionsToGenerate.map((extension) => ({
1357
+ extension,
1358
+ path: getFilepathForFile(coreCompiler, outDir, componentName, extension),
1359
+ }));
1360
+ await checkForOverwrite(filesToGenerate, config);
1361
+ const writtenFiles = await Promise.all(filesToGenerate.map((file) => getBoilerplateAndWriteFile(config, componentName, extensionsToGenerate.includes('css'), file))).catch((error) => config.logger.error(error));
1341
1362
  if (!writtenFiles) {
1342
1363
  return config.sys.exit(1);
1343
1364
  }
1365
+ // TODO(STENCIL-424): Investigate moving these console.log calls to config.logger.info
1344
1366
  console.log();
1345
1367
  console.log(`${config.logger.gray('$')} stencil generate ${input}`);
1346
1368
  console.log();
@@ -1350,6 +1372,9 @@ const taskGenerate = async (coreCompiler, config) => {
1350
1372
  };
1351
1373
  /**
1352
1374
  * Show a checkbox prompt to select the files to be generated.
1375
+ *
1376
+ * @returns a read-only array of `GenerableExtension`, the extensions that the user has decided
1377
+ * to generate
1353
1378
  */
1354
1379
  const chooseFilesToGenerate = async () => {
1355
1380
  const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
@@ -1365,22 +1390,76 @@ const chooseFilesToGenerate = async () => {
1365
1390
  })).filesToGenerate;
1366
1391
  };
1367
1392
  /**
1368
- * Get a file's boilerplate by its extension and write it to disk.
1393
+ * Get a filepath for a file we want to generate!
1394
+ *
1395
+ * The filepath for a given file depends on the path, the user-supplied
1396
+ * component name, the extension, and whether we're inside of a test directory.
1397
+ *
1398
+ * @param coreCompiler the compiler we're using, here to acces the `.path` module
1399
+ * @param path path to where we're going to generate the component
1400
+ * @param componentName the user-supplied name for the generated component
1401
+ * @param extension the file extension
1402
+ * @returns the full filepath to the component (with a possible `test` directory
1403
+ * added)
1404
+ */
1405
+ const getFilepathForFile = (coreCompiler, path, componentName, extension) => isTest(extension)
1406
+ ? coreCompiler.path.join(path, 'test', `${componentName}.${extension}`)
1407
+ : coreCompiler.path.join(path, `${componentName}.${extension}`);
1408
+ /**
1409
+ * Get the boilerplate for a file and write it to disk
1410
+ *
1411
+ * @param config the current config, needed for file operations
1412
+ * @param componentName the component name (user-supplied)
1413
+ * @param withCss are we generating CSS?
1414
+ * @param file the file we want to write
1415
+ * @returns a `Promise<string>` which holds the full filepath we've written to,
1416
+ * used to print out a little summary of our activity to the user.
1369
1417
  */
1370
- const writeFileByExtension = async (coreCompiler, config, path, name, extension, withCss) => {
1371
- if (isTest(extension)) {
1372
- path = coreCompiler.path.join(path, 'test');
1373
- }
1374
- const outFile = coreCompiler.path.join(path, `${name}.${extension}`);
1375
- const boilerplate = getBoilerplateByExtension(name, extension, withCss);
1376
- await config.sys.writeFile(outFile, boilerplate);
1377
- return outFile;
1418
+ const getBoilerplateAndWriteFile = async (config, componentName, withCss, file) => {
1419
+ const boilerplate = getBoilerplateByExtension(componentName, file.extension, withCss);
1420
+ await config.sys.writeFile(file.path, boilerplate);
1421
+ return file.path;
1378
1422
  };
1423
+ /**
1424
+ * Check to see if any of the files we plan to write already exist and would
1425
+ * therefore be overwritten if we proceed, because we'd like to not overwrite
1426
+ * people's code!
1427
+ *
1428
+ * This function will check all the filepaths and if it finds any files log an
1429
+ * error and exit with an error code. If it doesn't find anything it will just
1430
+ * peacefully return `Promise<void>`.
1431
+ *
1432
+ * @param files the files we want to check
1433
+ * @param config the Config object, used here to get access to `sys.readFile`
1434
+ */
1435
+ const checkForOverwrite = async (files, config) => {
1436
+ const alreadyPresent = [];
1437
+ await Promise.all(files.map(async ({ path }) => {
1438
+ if ((await config.sys.readFile(path)) !== undefined) {
1439
+ alreadyPresent.push(path);
1440
+ }
1441
+ }));
1442
+ if (alreadyPresent.length > 0) {
1443
+ config.logger.error('Generating code would overwrite the following files:', ...alreadyPresent.map((path) => '\t' + path));
1444
+ await config.sys.exit(1);
1445
+ }
1446
+ };
1447
+ /**
1448
+ * Check if an extension is for a test
1449
+ *
1450
+ * @param extension the extension we want to check
1451
+ * @returns a boolean indicating whether or not its a test
1452
+ */
1379
1453
  const isTest = (extension) => {
1380
1454
  return extension === 'e2e.ts' || extension === 'spec.tsx';
1381
1455
  };
1382
1456
  /**
1383
1457
  * Get the boilerplate for a file by its extension.
1458
+ *
1459
+ * @param tagName the name of the component we're generating
1460
+ * @param extension the file extension we want boilerplate for (.css, tsx, etc)
1461
+ * @param withCss a boolean indicating whether we're generating a CSS file
1462
+ * @returns a string container the file boilerplate for the supplied extension
1384
1463
  */
1385
1464
  const getBoilerplateByExtension = (tagName, extension, withCss) => {
1386
1465
  switch (extension) {
@@ -1397,7 +1476,10 @@ const getBoilerplateByExtension = (tagName, extension, withCss) => {
1397
1476
  }
1398
1477
  };
1399
1478
  /**
1400
- * Get the boilerplate for a component.
1479
+ * Get the boilerplate for a file containing the definition of a component
1480
+ * @param tagName the name of the tag to give the component
1481
+ * @param hasStyle designates if the component has an external stylesheet or not
1482
+ * @returns the contents of a file that defines a component
1401
1483
  */
1402
1484
  const getComponentBoilerplate = (tagName, hasStyle) => {
1403
1485
  const decorator = [`{`];
@@ -1431,7 +1513,9 @@ const getStyleUrlBoilerplate = () => `:host {
1431
1513
  }
1432
1514
  `;
1433
1515
  /**
1434
- * Get the boilerplate for a spec test.
1516
+ * Get the boilerplate for a file containing a spec (unit) test for a component
1517
+ * @param tagName the name of the tag associated with the component under test
1518
+ * @returns the contents of a file that unit tests a component
1435
1519
  */
1436
1520
  const getSpecTestBoilerplate = (tagName) => `import { newSpecPage } from '@stencil/core/testing';
1437
1521
  import { ${toPascalCase(tagName)} } from '../${tagName}';
@@ -1453,22 +1537,26 @@ describe('${tagName}', () => {
1453
1537
  });
1454
1538
  `;
1455
1539
  /**
1456
- * Get the boilerplate for an E2E test.
1540
+ * Get the boilerplate for a file containing an end-to-end (E2E) test for a component
1541
+ * @param tagName the name of the tag associated with the component under test
1542
+ * @returns the contents of a file that E2E tests a component
1457
1543
  */
1458
- const getE2eTestBoilerplate = (name) => `import { newE2EPage } from '@stencil/core/testing';
1544
+ const getE2eTestBoilerplate = (tagName) => `import { newE2EPage } from '@stencil/core/testing';
1459
1545
 
1460
- describe('${name}', () => {
1546
+ describe('${tagName}', () => {
1461
1547
  it('renders', async () => {
1462
1548
  const page = await newE2EPage();
1463
- await page.setContent('<${name}></${name}>');
1549
+ await page.setContent('<${tagName}></${tagName}>');
1464
1550
 
1465
- const element = await page.find('${name}');
1551
+ const element = await page.find('${tagName}');
1466
1552
  expect(element).toHaveClass('hydrated');
1467
1553
  });
1468
1554
  });
1469
1555
  `;
1470
1556
  /**
1471
1557
  * Convert a dash case string to pascal case.
1558
+ * @param str the string to convert
1559
+ * @returns the converted input as pascal case
1472
1560
  */
1473
1561
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1474
1562
 
package/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Stencil CLI v2.15.1 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI v2.16.1-0 | MIT Licensed | https://stenciljs.com
3
3
  */
4
4
  const toLowerCase = (str) => str.toLowerCase();
5
5
  const dashToPascalCase = (str) => toLowerCase(str)
@@ -94,6 +94,8 @@ const TASK_CANCELED_MSG = `task canceled`;
94
94
  * Forward-slash paths can be used in Windows as long as they're not
95
95
  * extended-length paths and don't contain any non-ascii characters.
96
96
  * This was created since the path methods in Node.js outputs \\ paths on Windows.
97
+ * @param path the Windows-based path to convert
98
+ * @returns the converted path
97
99
  */
98
100
  const normalizePath = (path) => {
99
101
  if (typeof path !== 'string') {
@@ -506,7 +508,7 @@ const getNpmConfigEnvArgs = (sys) => {
506
508
  const dependencies = [
507
509
  {
508
510
  name: "@stencil/core",
509
- version: "2.15.1",
511
+ version: "2.16.1-0",
510
512
  main: "compiler/stencil.js",
511
513
  resources: [
512
514
  "package.json",
@@ -943,10 +945,10 @@ async function updateConfig(sys, newOptions) {
943
945
 
944
946
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
945
947
  const DOCS_CUSTOM = 'docs-custom';
946
- const DOCS_JSON = `docs-json`;
947
- const DOCS_README = `docs-readme`;
948
- const DOCS_VSCODE = `docs-vscode`;
949
- const WWW = `www`;
948
+ const DOCS_JSON = 'docs-json';
949
+ const DOCS_README = 'docs-readme';
950
+ const DOCS_VSCODE = 'docs-vscode';
951
+ const WWW = 'www';
950
952
 
951
953
  /**
952
954
  * Used to within taskBuild to provide the component_count property.
@@ -1048,10 +1050,12 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1048
1050
  };
1049
1051
  };
1050
1052
  /**
1051
- * Reads package-lock.json, yarn.lock, and package.json files in order to cross reference
1053
+ * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1052
1054
  * the dependencies and devDependencies properties. Pulls up the current installed version
1053
1055
  * of each package under the @stencil, @ionic, and @capacitor scopes.
1054
- * @returns string[]
1056
+ * @param sys the system instance where telemetry is invoked
1057
+ * @param config the Stencil configuration associated with the current task that triggered telemetry
1058
+ * @returns an object listing all dev and production dependencies under the aforementioned scopes
1055
1059
  */
1056
1060
  async function getInstalledPackages(sys, config) {
1057
1061
  let packages = [];
@@ -1130,7 +1134,12 @@ function sanitizeDeclaredVersion(version) {
1130
1134
  return version.replace(/[*^~]/g, '');
1131
1135
  }
1132
1136
  /**
1133
- * If telemetry is enabled, send a metric via IPC to a forked process for uploading.
1137
+ * If telemetry is enabled, send a metric to an external data store
1138
+ * @param sys the system instance where telemetry is invoked
1139
+ * @param config the Stencil configuration associated with the current task that triggered telemetry
1140
+ * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
1141
+ * "Stencil Version". For example, "stencil_cli_command" is a name that is used to track all CLI command information.
1142
+ * @param value the data to send to the external data store under the provided name argument
1134
1143
  */
1135
1144
  async function sendMetric(sys, config, name, value) {
1136
1145
  const session_id = await getTelemetryToken(sys);
@@ -1283,7 +1292,14 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1283
1292
  (!global.origin || typeof global.origin !== 'string');
1284
1293
 
1285
1294
  /**
1286
- * Task to generate component boilerplate.
1295
+ * Task to generate component boilerplate and write it to disk. This task can
1296
+ * cause the program to exit with an error under various circumstances, such as
1297
+ * being called in an inappropriate place, being asked to overwrite files that
1298
+ * already exist, etc.
1299
+ *
1300
+ * @param coreCompiler the CoreCompiler we're using currently, here we're
1301
+ * mainly accessing the `path` module
1302
+ * @param config the user-supplied config, which we need here to access `.sys`.
1287
1303
  */
1288
1304
  const taskGenerate = async (coreCompiler, config) => {
1289
1305
  if (!IS_NODE_ENV) {
@@ -1313,10 +1329,16 @@ const taskGenerate = async (coreCompiler, config) => {
1313
1329
  const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
1314
1330
  const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
1315
1331
  await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
1316
- const writtenFiles = await Promise.all(extensionsToGenerate.map((extension) => writeFileByExtension(coreCompiler, config, outDir, componentName, extension, extensionsToGenerate.includes('css')))).catch((error) => config.logger.error(error));
1332
+ const filesToGenerate = extensionsToGenerate.map((extension) => ({
1333
+ extension,
1334
+ path: getFilepathForFile(coreCompiler, outDir, componentName, extension),
1335
+ }));
1336
+ await checkForOverwrite(filesToGenerate, config);
1337
+ const writtenFiles = await Promise.all(filesToGenerate.map((file) => getBoilerplateAndWriteFile(config, componentName, extensionsToGenerate.includes('css'), file))).catch((error) => config.logger.error(error));
1317
1338
  if (!writtenFiles) {
1318
1339
  return config.sys.exit(1);
1319
1340
  }
1341
+ // TODO(STENCIL-424): Investigate moving these console.log calls to config.logger.info
1320
1342
  console.log();
1321
1343
  console.log(`${config.logger.gray('$')} stencil generate ${input}`);
1322
1344
  console.log();
@@ -1326,6 +1348,9 @@ const taskGenerate = async (coreCompiler, config) => {
1326
1348
  };
1327
1349
  /**
1328
1350
  * Show a checkbox prompt to select the files to be generated.
1351
+ *
1352
+ * @returns a read-only array of `GenerableExtension`, the extensions that the user has decided
1353
+ * to generate
1329
1354
  */
1330
1355
  const chooseFilesToGenerate = async () => {
1331
1356
  const { prompt } = await import('../sys/node/prompts.js');
@@ -1341,22 +1366,76 @@ const chooseFilesToGenerate = async () => {
1341
1366
  })).filesToGenerate;
1342
1367
  };
1343
1368
  /**
1344
- * Get a file's boilerplate by its extension and write it to disk.
1369
+ * Get a filepath for a file we want to generate!
1370
+ *
1371
+ * The filepath for a given file depends on the path, the user-supplied
1372
+ * component name, the extension, and whether we're inside of a test directory.
1373
+ *
1374
+ * @param coreCompiler the compiler we're using, here to acces the `.path` module
1375
+ * @param path path to where we're going to generate the component
1376
+ * @param componentName the user-supplied name for the generated component
1377
+ * @param extension the file extension
1378
+ * @returns the full filepath to the component (with a possible `test` directory
1379
+ * added)
1380
+ */
1381
+ const getFilepathForFile = (coreCompiler, path, componentName, extension) => isTest(extension)
1382
+ ? coreCompiler.path.join(path, 'test', `${componentName}.${extension}`)
1383
+ : coreCompiler.path.join(path, `${componentName}.${extension}`);
1384
+ /**
1385
+ * Get the boilerplate for a file and write it to disk
1386
+ *
1387
+ * @param config the current config, needed for file operations
1388
+ * @param componentName the component name (user-supplied)
1389
+ * @param withCss are we generating CSS?
1390
+ * @param file the file we want to write
1391
+ * @returns a `Promise<string>` which holds the full filepath we've written to,
1392
+ * used to print out a little summary of our activity to the user.
1345
1393
  */
1346
- const writeFileByExtension = async (coreCompiler, config, path, name, extension, withCss) => {
1347
- if (isTest(extension)) {
1348
- path = coreCompiler.path.join(path, 'test');
1349
- }
1350
- const outFile = coreCompiler.path.join(path, `${name}.${extension}`);
1351
- const boilerplate = getBoilerplateByExtension(name, extension, withCss);
1352
- await config.sys.writeFile(outFile, boilerplate);
1353
- return outFile;
1394
+ const getBoilerplateAndWriteFile = async (config, componentName, withCss, file) => {
1395
+ const boilerplate = getBoilerplateByExtension(componentName, file.extension, withCss);
1396
+ await config.sys.writeFile(file.path, boilerplate);
1397
+ return file.path;
1354
1398
  };
1399
+ /**
1400
+ * Check to see if any of the files we plan to write already exist and would
1401
+ * therefore be overwritten if we proceed, because we'd like to not overwrite
1402
+ * people's code!
1403
+ *
1404
+ * This function will check all the filepaths and if it finds any files log an
1405
+ * error and exit with an error code. If it doesn't find anything it will just
1406
+ * peacefully return `Promise<void>`.
1407
+ *
1408
+ * @param files the files we want to check
1409
+ * @param config the Config object, used here to get access to `sys.readFile`
1410
+ */
1411
+ const checkForOverwrite = async (files, config) => {
1412
+ const alreadyPresent = [];
1413
+ await Promise.all(files.map(async ({ path }) => {
1414
+ if ((await config.sys.readFile(path)) !== undefined) {
1415
+ alreadyPresent.push(path);
1416
+ }
1417
+ }));
1418
+ if (alreadyPresent.length > 0) {
1419
+ config.logger.error('Generating code would overwrite the following files:', ...alreadyPresent.map((path) => '\t' + path));
1420
+ await config.sys.exit(1);
1421
+ }
1422
+ };
1423
+ /**
1424
+ * Check if an extension is for a test
1425
+ *
1426
+ * @param extension the extension we want to check
1427
+ * @returns a boolean indicating whether or not its a test
1428
+ */
1355
1429
  const isTest = (extension) => {
1356
1430
  return extension === 'e2e.ts' || extension === 'spec.tsx';
1357
1431
  };
1358
1432
  /**
1359
1433
  * Get the boilerplate for a file by its extension.
1434
+ *
1435
+ * @param tagName the name of the component we're generating
1436
+ * @param extension the file extension we want boilerplate for (.css, tsx, etc)
1437
+ * @param withCss a boolean indicating whether we're generating a CSS file
1438
+ * @returns a string container the file boilerplate for the supplied extension
1360
1439
  */
1361
1440
  const getBoilerplateByExtension = (tagName, extension, withCss) => {
1362
1441
  switch (extension) {
@@ -1373,7 +1452,10 @@ const getBoilerplateByExtension = (tagName, extension, withCss) => {
1373
1452
  }
1374
1453
  };
1375
1454
  /**
1376
- * Get the boilerplate for a component.
1455
+ * Get the boilerplate for a file containing the definition of a component
1456
+ * @param tagName the name of the tag to give the component
1457
+ * @param hasStyle designates if the component has an external stylesheet or not
1458
+ * @returns the contents of a file that defines a component
1377
1459
  */
1378
1460
  const getComponentBoilerplate = (tagName, hasStyle) => {
1379
1461
  const decorator = [`{`];
@@ -1407,7 +1489,9 @@ const getStyleUrlBoilerplate = () => `:host {
1407
1489
  }
1408
1490
  `;
1409
1491
  /**
1410
- * Get the boilerplate for a spec test.
1492
+ * Get the boilerplate for a file containing a spec (unit) test for a component
1493
+ * @param tagName the name of the tag associated with the component under test
1494
+ * @returns the contents of a file that unit tests a component
1411
1495
  */
1412
1496
  const getSpecTestBoilerplate = (tagName) => `import { newSpecPage } from '@stencil/core/testing';
1413
1497
  import { ${toPascalCase(tagName)} } from '../${tagName}';
@@ -1429,22 +1513,26 @@ describe('${tagName}', () => {
1429
1513
  });
1430
1514
  `;
1431
1515
  /**
1432
- * Get the boilerplate for an E2E test.
1516
+ * Get the boilerplate for a file containing an end-to-end (E2E) test for a component
1517
+ * @param tagName the name of the tag associated with the component under test
1518
+ * @returns the contents of a file that E2E tests a component
1433
1519
  */
1434
- const getE2eTestBoilerplate = (name) => `import { newE2EPage } from '@stencil/core/testing';
1520
+ const getE2eTestBoilerplate = (tagName) => `import { newE2EPage } from '@stencil/core/testing';
1435
1521
 
1436
- describe('${name}', () => {
1522
+ describe('${tagName}', () => {
1437
1523
  it('renders', async () => {
1438
1524
  const page = await newE2EPage();
1439
- await page.setContent('<${name}></${name}>');
1525
+ await page.setContent('<${tagName}></${tagName}>');
1440
1526
 
1441
- const element = await page.find('${name}');
1527
+ const element = await page.find('${tagName}');
1442
1528
  expect(element).toHaveClass('hydrated');
1443
1529
  });
1444
1530
  });
1445
1531
  `;
1446
1532
  /**
1447
1533
  * Convert a dash case string to pascal case.
1534
+ * @param str the string to convert
1535
+ * @returns the converted input as pascal case
1448
1536
  */
1449
1537
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1450
1538
 
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stencil/core/cli",
3
- "version": "2.15.1",
3
+ "version": "2.16.1-0",
4
4
  "description": "Stencil CLI.",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stencil/core/compiler",
3
- "version": "2.15.1",
3
+ "version": "2.16.1-0",
4
4
  "description": "Stencil Compiler.",
5
5
  "main": "./stencil.js",
6
6
  "types": "./stencil.d.ts",