@citolab/tspci 2.8.4 → 2.9.9

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.
@@ -46,6 +46,19 @@ node -e "
46
46
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
47
47
  " "${TARGET_DIR}/package.json" "file:${PACK_PATH}"
48
48
 
49
+ echo "Verifying generated package.json contains required dev dependencies..."
50
+ node -e "
51
+ const fs = require('fs');
52
+ const pkgPath = process.argv[1];
53
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
54
+ const hasTslib = Boolean(pkg.devDependencies && pkg.devDependencies.tslib);
55
+ if (!hasTslib) {
56
+ console.error('ERROR: expected devDependency \"tslib\" in generated package.json');
57
+ process.exit(1);
58
+ }
59
+ console.log('OK: required devDependency \"tslib\" is present');
60
+ " "${TARGET_DIR}/package.json"
61
+
49
62
  echo ""
50
63
  echo "Generated files:"
51
64
  find "${TARGET_DIR}" -maxdepth 3 -type f | sort
@@ -68,7 +81,6 @@ echo ""
68
81
  echo "Installing dependencies in generated project..."
69
82
  (cd "${TARGET_DIR}" && npm install --cache "${NPM_CACHE_DIR}" --workspaces=false)
70
83
 
71
- echo ""
72
84
  echo "Running production build smoke test..."
73
85
  node -e "
74
86
  const { spawnSync } = require('child_process');
@@ -76,6 +88,7 @@ node -e "
76
88
  const result = spawnSync('npm', ['run', 'prod'], {
77
89
  cwd: targetDir,
78
90
  stdio: 'inherit',
91
+ shell: process.platform === 'win32',
79
92
  timeout: 180000
80
93
  });
81
94
  if (result.error) {
@@ -99,6 +112,41 @@ if [[ ! -f "${DIST_TYPES}" ]]; then
99
112
  exit 1
100
113
  fi
101
114
 
115
+ echo "Running dev startup smoke test (timeout 20s)..."
116
+ node -e "
117
+ const { spawn } = require('child_process');
118
+ const targetDir = process.argv[1];
119
+ const child = spawn('npm', ['run', 'dev'], {
120
+ cwd: targetDir,
121
+ shell: process.platform === 'win32'
122
+ });
123
+ let logs = '';
124
+ const onData = (chunk) => {
125
+ logs += chunk.toString();
126
+ };
127
+ child.stdout.on('data', onData);
128
+ child.stderr.on('data', onData);
129
+ child.on('error', (err) => {
130
+ console.error(err.message);
131
+ process.exit(1);
132
+ });
133
+ setTimeout(() => {
134
+ child.kill('SIGTERM');
135
+ const hasReady = logs.includes('Ready for changes');
136
+ const missingTslib = logs.includes(\"Could not find module 'tslib'\");
137
+ if (missingTslib) {
138
+ console.error('ERROR: dev startup failed due to missing tslib');
139
+ process.exit(1);
140
+ }
141
+ if (!hasReady) {
142
+ console.error('ERROR: dev startup did not reach \"Ready for changes\" within timeout');
143
+ console.error(logs);
144
+ process.exit(1);
145
+ }
146
+ process.exit(0);
147
+ }, 20000);
148
+ " "${TARGET_DIR}"
149
+
102
150
  echo ""
103
151
  echo "OK: found expected entry file: ${EXPECTED_FILE}"
104
152
  echo "OK: production build output exists: ${DIST_JS}"
@@ -88,6 +88,17 @@ create_project() {
88
88
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
89
89
  ' "${project_dir}/package.json" "$(npm_path "${TSPCI_TGZ}")" "$(npm_path "${PREACT_STORE_TGZ}")"
90
90
 
91
+ node -e '
92
+ const fs = require("fs");
93
+ const pkgPath = process.argv[1];
94
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
95
+ const hasTslib = Boolean(pkg.devDependencies && pkg.devDependencies.tslib);
96
+ if (!hasTslib) {
97
+ console.error("ERROR: expected devDependency \"tslib\" in generated package.json");
98
+ process.exit(1);
99
+ }
100
+ ' "${project_dir}/package.json"
101
+
91
102
  (cd "${project_dir}" && npm install --cache "${NPM_CACHE_DIR}" --workspaces=false)
92
103
  }
93
104
 
@@ -105,6 +116,43 @@ run_base_build() {
105
116
  assert_file "${project_dir}/dist/index.js"
106
117
  }
107
118
 
119
+ run_dev_start_smoke() {
120
+ local project_dir="$1"
121
+ node -e '
122
+ const { spawn } = require("child_process");
123
+ const targetDir = process.argv[1];
124
+ const child = spawn("npm", ["run", "dev"], {
125
+ cwd: targetDir,
126
+ shell: process.platform === "win32"
127
+ });
128
+ let logs = "";
129
+ const onData = (chunk) => {
130
+ logs += chunk.toString();
131
+ };
132
+ child.stdout.on("data", onData);
133
+ child.stderr.on("data", onData);
134
+ child.on("error", (err) => {
135
+ console.error(err.message);
136
+ process.exit(1);
137
+ });
138
+ setTimeout(() => {
139
+ child.kill("SIGTERM");
140
+ const hasReady = logs.includes("Ready for changes");
141
+ const missingTslib = logs.includes("Could not find module '\''tslib'\''");
142
+ if (missingTslib) {
143
+ console.error("ERROR: dev startup failed due to missing tslib");
144
+ process.exit(1);
145
+ }
146
+ if (!hasReady) {
147
+ console.error("ERROR: dev startup did not reach \"Ready for changes\" within timeout");
148
+ console.error(logs);
149
+ process.exit(1);
150
+ }
151
+ process.exit(0);
152
+ }, 20000);
153
+ ' "${project_dir}"
154
+ }
155
+
108
156
  echo "Scenario 1: vanilla PCI creates and builds"
109
157
  create_project "VanillaPci" "javascript" "Vanilla PCI matrix test"
110
158
  assert_file "${TEST_ROOT}/VanillaPci/src/index.js"
@@ -114,6 +162,7 @@ echo "Scenario 2: TypeScript PCI (without tailwind/preact) creates and builds"
114
162
  create_project "TypeScriptPci" "typescript" "TypeScript PCI matrix test"
115
163
  assert_file "${TEST_ROOT}/TypeScriptPci/src/index.ts"
116
164
  run_base_build "${TEST_ROOT}/TypeScriptPci"
165
+ run_dev_start_smoke "${TEST_ROOT}/TypeScriptPci"
117
166
 
118
167
  echo "Scenario 3: TypeScript + tailwind PCI creates and builds"
119
168
  create_project "TypeScriptTailwindPci" "preact+tailwind" "TypeScript + tailwind PCI matrix test"
package/src/cli.js CHANGED
@@ -9,7 +9,8 @@ import fs from "fs";
9
9
  import os from "os";
10
10
  import clear from "clear";
11
11
  import figlet from "figlet";
12
- import inquirer from "inquirer";
12
+ import chokidar from "chokidar";
13
+ import { select } from "@inquirer/prompts";
13
14
 
14
15
  const args = process.argv.slice(2);
15
16
 
@@ -21,6 +22,70 @@ const __dirname = path.dirname(__filename);
21
22
  const PACKAGE_ROOT_PATH = process.cwd();
22
23
  const PACKAGE_DIST_PATH = path.join(PACKAGE_ROOT_PATH, "dist");
23
24
  const NODE_MODULES = path.join(process.cwd(), "node_modules");
25
+ const REQUIRED_DEV_DEPENDENCIES = {
26
+ tslib: "^2.8.1",
27
+ };
28
+
29
+ const ensureRequiredDevDependencies = (packageJson) => {
30
+ packageJson.devDependencies = packageJson.devDependencies || {};
31
+ for (const [name, version] of Object.entries(REQUIRED_DEV_DEPENDENCIES)) {
32
+ if (!packageJson.devDependencies[name]) {
33
+ packageJson.devDependencies[name] = version;
34
+ }
35
+ }
36
+ };
37
+
38
+ const isMissingTslibError = (event) => {
39
+ const error = event?.error;
40
+ const message = [error?.message, error?.toString?.(), event?.message].filter(Boolean).join("\n");
41
+ return message.includes("Could not find module 'tslib'");
42
+ };
43
+
44
+ const getDevIndexHtmlSourceCandidates = (target = "IMS") => {
45
+ const projectSource = path.join(PACKAGE_ROOT_PATH, "src/index.html");
46
+ const templateSource =
47
+ target === "IMS"
48
+ ? path.join(__dirname, "../public/index.html")
49
+ : path.join(NODE_MODULES, `@citolab/tspci-${target}/public/index.html`);
50
+ return { projectSource, templateSource };
51
+ };
52
+
53
+ const resolveActiveDevIndexHtmlSource = (target = "IMS") => {
54
+ const { projectSource, templateSource } = getDevIndexHtmlSourceCandidates(target);
55
+ return fs.existsSync(projectSource) ? projectSource : templateSource;
56
+ };
57
+
58
+ const copyDevIndexHtmlToDist = (target = "IMS", reason = "initial") => {
59
+ const source = resolveActiveDevIndexHtmlSource(target);
60
+ if (!fs.existsSync(source)) {
61
+ throw new Error(`No index.html template found for dev mode. Expected: ${source}`);
62
+ }
63
+ fs.copyFileSync(source, path.join(PACKAGE_DIST_PATH, "index.html"));
64
+ console.log(`index.html copied to dist (${reason}) from ${source}`);
65
+ };
66
+
67
+ const watchDevIndexHtmlSources = (target = "IMS") => {
68
+ const { projectSource, templateSource } = getDevIndexHtmlSourceCandidates(target);
69
+ const watcher = chokidar.watch([projectSource, templateSource], {
70
+ ignoreInitial: true,
71
+ awaitWriteFinish: {
72
+ stabilityThreshold: 120,
73
+ pollInterval: 20,
74
+ },
75
+ });
76
+
77
+ watcher.on("all", (event, changedPath) => {
78
+ try {
79
+ copyDevIndexHtmlToDist(target, `${event}: ${changedPath}`);
80
+ } catch (err) {
81
+ console.error(`Failed to refresh index.html after ${event} on ${changedPath}`, err);
82
+ }
83
+ });
84
+
85
+ const closeWatcher = () => watcher.close();
86
+ process.on("SIGINT", closeWatcher);
87
+ process.on("SIGTERM", closeWatcher);
88
+ };
24
89
 
25
90
  // https://rollupjs.org/guide/en/#programmatically-loading-a-config-file
26
91
  const watchBuild = (devServer = false, target = "IMS") => {
@@ -36,27 +101,8 @@ const watchBuild = (devServer = false, target = "IMS") => {
36
101
  }
37
102
  if (devServer) {
38
103
  try {
39
- if (fs.existsSync(path.join(PACKAGE_ROOT_PATH, "src/index.html"))) {
40
- fs.copyFile(
41
- path.join(PACKAGE_ROOT_PATH, "src/index.html"),
42
- path.join(PACKAGE_DIST_PATH, "index.html"),
43
- (err) => {
44
- if (err) throw err;
45
- console.log("using index.html found in src/index.html, copied to dist");
46
- }
47
- );
48
- } else {
49
- let fileToCopy;
50
- if (target == "IMS") {
51
- fileToCopy = path.join(__dirname, `../public/index.html`);
52
- } else {
53
- fileToCopy = path.join(NODE_MODULES, `@citolab/tspci-${target}/public/index.html`);
54
- }
55
- fs.copyFile(fileToCopy, path.join(PACKAGE_DIST_PATH, "index.html"), (err) => {
56
- if (err) throw err;
57
- console.log("using index.html was copied to /dist folder pci");
58
- });
59
- }
104
+ copyDevIndexHtmlToDist(target);
105
+ watchDevIndexHtmlSources(target);
60
106
  } catch (err) {
61
107
  console.error(err);
62
108
  }
@@ -70,6 +116,10 @@ const watchBuild = (devServer = false, target = "IMS") => {
70
116
  console.log("starting build");
71
117
  }
72
118
  if (event.code == "ERROR") {
119
+ if (isMissingTslibError(event)) {
120
+ console.log(chalk.red("ERROR: Missing dependency 'tslib'"));
121
+ console.log(chalk.yellow("Install it in your PCI project: npm install --save-dev tslib"));
122
+ }
73
123
  console.log("ERROR", event);
74
124
  }
75
125
  });
@@ -110,16 +160,13 @@ const prodBuild = async () => {
110
160
  });
111
161
  };
112
162
 
113
- function selectOptions(prompt, options) {
114
- return inquirer.prompt([
115
- {
116
- type: "list",
117
- message: prompt,
118
- name: "value",
119
- choices: options,
120
- default: options[0],
121
- },
122
- ]);
163
+ async function selectOptions(prompt, options) {
164
+ const value = await select({
165
+ message: prompt,
166
+ choices: options.map((o) => ({ value: o })),
167
+ default: options[0],
168
+ });
169
+ return { value };
123
170
  }
124
171
 
125
172
  function askOption(prompt) {
@@ -312,6 +359,7 @@ const run = async () => {
312
359
  const packageJson = path.join(destinationFolder, "package.json");
313
360
  const packageJsonContent = fs.readFileSync(packageJson, "utf8");
314
361
  let packageJsonContentJson = JSON.parse(packageJsonContent);
362
+ ensureRequiredDevDependencies(packageJsonContentJson);
315
363
 
316
364
  const kebabCasedProjectName = pciName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
317
365
  packageJsonContentJson.name = kebabCasedProjectName;
@@ -418,9 +466,9 @@ const run = async () => {
418
466
  execSync("npm install", { cwd: destinationFolder, stdio: [0, 1, 2] });
419
467
  execSync("npm install", { cwd: destinationFolder, stdio: [0, 1, 2] }); // Somehow this to prevent the fsevents.watch is not a function error
420
468
  console.log(chalk.green(`📦 Done!`));
421
- console.log(chalk.blueBright(`Navigate to ${destinationFolder} or open this folder (e.g. with vscode).`));
422
- console.log(chalk.blueBright(`Start the dev server with: npm run dev`));
423
- console.log(chalk.blueBright(`Add support for TAO with: npm run tspci -- add --target tao`));
469
+ console.log(`Navigate to ${chalk.bold(destinationFolder)} or open this folder (e.g. with vscode).`);
470
+ console.log(`Start the dev server with: ${chalk.bold("npm run dev")}`);
471
+ console.log(`Add support for TAO with: ${chalk.bold("npm run tspci -- add --target tao")}`);
424
472
 
425
473
  return;
426
474
  } else {