@corva/create-app 0.0.0-2576df8 → 0.0.0-5fb771a

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 (58) hide show
  1. package/README.md +8 -6
  2. package/common/python/Makefile +8 -5
  3. package/common/python/pyproject.toml +25 -0
  4. package/common/python/uv.lock +381 -0
  5. package/common/ui/config/vite/corva-vite-config.mjs +352 -0
  6. package/common/ui/devApp.js +1 -0
  7. package/common/ui/index.html +17 -0
  8. package/common/ui/isolatedApp.js +1 -0
  9. package/common/ui/public/DevCenterIsolatedAppPage.html +26 -0
  10. package/common/ui/public/favicon.ico +0 -0
  11. package/common/ui/vite.config.mjs +3 -0
  12. package/common/ui/vitest.global-setup.js +4 -0
  13. package/common/ui/vitest.setup.js +24 -0
  14. package/lib/commands/create.js +11 -3
  15. package/lib/commands/release.js +17 -2
  16. package/lib/constants/cli.js +5 -8
  17. package/lib/constants/manifest.js +3 -3
  18. package/lib/constants/package.js +75 -65
  19. package/lib/flow.js +6 -0
  20. package/lib/flows/lib/manifest.js +1 -1
  21. package/lib/flows/release.js +4 -0
  22. package/lib/flows/steps/release/resolve-prebuilt-zip.js +35 -0
  23. package/lib/flows/steps/zip-file-list-resolve.js +3 -2
  24. package/lib/flows/zip-simple.js +2 -0
  25. package/lib/helpers/cli-version.js +14 -1
  26. package/lib/helpers/resolve-app-runtime.js +25 -14
  27. package/lib/helpers/utils.js +7 -3
  28. package/lib/main.js +2 -3
  29. package/lib/options/zip-file-name.js +18 -0
  30. package/package.json +1 -1
  31. package/templates/ui/javascript/.eslintrc +1 -2
  32. package/templates/ui/javascript/AGENTS.md +13 -11
  33. package/templates/ui/javascript/src/{App.completion.js → App.completion.jsx} +3 -3
  34. package/templates/ui/javascript/src/{App.drilling.js → App.drilling.jsx} +3 -3
  35. package/templates/ui/javascript/src/{AppSettings.js → AppSettings.jsx} +1 -1
  36. package/templates/ui/javascript/src/__tests__/{AppSettings.test.js → AppSettings.test.jsx} +1 -1
  37. package/templates/ui/typescript/AGENTS.md +10 -8
  38. package/templates/ui/typescript/src/App.completion.tsx +1 -1
  39. package/templates/ui/typescript/src/App.drilling.tsx +1 -1
  40. package/templates/ui/typescript/src/__tests__/AppSettings.test.tsx +1 -1
  41. package/templates/ui/typescript/tsconfig.json +17 -4
  42. package/common/python/requirements.txt +0 -2
  43. package/templates/ui/javascript/config/jest/babelTransform.js +0 -16
  44. package/templates/ui/javascript/config/jest/cssTransform.js +0 -16
  45. package/templates/ui/javascript/config/jest/fileTransform.js +0 -48
  46. package/templates/ui/javascript/config/jest/globalSetup.js +0 -5
  47. package/templates/ui/javascript/config/jest/setupTests.js +0 -30
  48. package/templates/ui/javascript/config-overrides.js +0 -10
  49. package/templates/ui/typescript/config/jest/babelTransform.js +0 -16
  50. package/templates/ui/typescript/config/jest/cssTransform.js +0 -16
  51. package/templates/ui/typescript/config/jest/fileTransform.js +0 -48
  52. package/templates/ui/typescript/config/jest/globalSetup.js +0 -5
  53. package/templates/ui/typescript/config/jest/setupTests.js +0 -30
  54. package/templates/ui/typescript/config-overrides.js +0 -10
  55. /package/templates/ui/javascript/src/{App.scss → App.module.scss} +0 -0
  56. /package/templates/ui/javascript/src/__tests__/{App.test.js → App.test.jsx} +0 -0
  57. /package/templates/ui/javascript/src/__tests__/{TestsExample.test.js → TestsExample.test.jsx} +0 -0
  58. /package/templates/ui/typescript/src/{App.scss → App.module.scss} +0 -0
@@ -12,47 +12,47 @@ const uiDependencies = {
12
12
  'corva-convert-units': '1.10.33',
13
13
  'highcharts': '10.3.3',
14
14
  'highcharts-react-official': '2.2.2',
15
- 'lodash': '^4.17.21',
16
- 'mapbox.js': '3.2.1',
15
+ 'lodash': '^4.18.1',
16
+ 'mapbox-gl': '3.16.0',
17
17
  'moment': '2.29.4',
18
18
  'moment-timezone': '0.5.43',
19
- 'react': '17.0.1',
20
- 'react-dom': '17.0.1',
19
+ 'react': '18.3.1',
20
+ 'react-dom': '18.3.1',
21
21
  };
22
22
 
23
23
  const jsUiDevDependencies = {
24
24
  '@corva/dc-platform-shared': 'latest',
25
25
  '@corva/eslint-config-browser': '0.2.2',
26
- '@testing-library/jest-dom': '^5.14.1',
27
- '@testing-library/react': '^12.1.5',
28
- '@testing-library/react-hooks': '^8.0.1',
29
- '@testing-library/user-event': '^13.2.1',
30
- 'babel-jest': '^27.4.2',
31
- 'babel-preset-react-app': '^10.0.1',
26
+ '@testing-library/jest-dom': '7.0.1',
27
+ '@testing-library/react': '16.3.2',
28
+ '@testing-library/user-event': '14.6.6',
29
+ '@vitejs/plugin-react': '6.1.1',
30
+ '@vitest/coverage-v8': '4.1.11',
31
+ 'dotenv': '16.4.7',
32
+ 'dotenv-expand': '11.0.7',
32
33
  'eslint': '7.32.0',
33
- 'identity-obj-proxy': '^3.0.0',
34
- 'jest': '^27.4.3',
35
- 'jest-watch-typeahead': '^1.0.0',
36
- 'postcss-loader': '4.1.0',
37
- 'sass': '^1.89.2',
38
- 'sass-loader': '^16.0.5',
34
+ 'jsdom': '22.1.0',
35
+ 'postcss-preset-env': '11.3.0',
36
+ 'rollup-plugin-license': '3.6.0',
37
+ 'sass-embedded': '1.100.0',
38
+ 'terser': '5.47.1',
39
+ 'vite': '8.3.0',
40
+ 'vite-plugin-static-copy': '4.1.0',
41
+ 'vitest': '4.1.11',
39
42
  };
40
43
 
41
44
  const tsUiDevDependencies = {
42
45
  ...jsUiDevDependencies,
43
- '@tsconfig/create-react-app': '1.0.2',
44
- '@types/lodash': '4.17.5',
46
+ '@types/lodash': '^4.17.25',
45
47
  '@types/material-ui': '0.21.9',
46
48
  '@types/minimatch': '3.0.5',
47
49
  '@types/mime': '3.0.1',
48
- '@types/node': '18.19.47',
49
- '@types/react': '^17.0.22',
50
- '@types/react-dom': '^17.0.9',
51
- '@types/jest': '^27.0.1',
50
+ '@types/node': '24.12.2',
51
+ '@types/react': '18.2.42',
52
+ '@types/react-dom': '^18.2',
52
53
  '@typescript-eslint/eslint-plugin': '^4.31.2',
53
54
  '@typescript-eslint/parser': '^4.31.2',
54
- 'ts-loader': '8.2.0',
55
- 'typescript': '4.6.3',
55
+ 'typescript': '^5.9.3',
56
56
  };
57
57
 
58
58
  const corvaUiExtension = {
@@ -90,13 +90,13 @@ function applyUiExtension(packageJson, extensionNames) {
90
90
  }
91
91
 
92
92
  const uiScripts = {
93
- build: 'webpack --config=./config-overrides.js --mode production',
94
- start: 'webpack-dev-server --config=./config-overrides.js --open --mode development',
93
+ build: 'vite build',
94
+ start: 'vite --open',
95
95
  zip: 'create-corva-app zip .',
96
96
  lint: 'eslint --cache ./src/',
97
97
  release: 'create-corva-app release .',
98
- test: 'jest',
99
- coverage: 'jest --coverage',
98
+ test: 'vitest run',
99
+ coverage: 'vitest run --coverage',
100
100
  };
101
101
 
102
102
  const uiPackage = {
@@ -105,49 +105,18 @@ const uiPackage = {
105
105
  'scripts': uiScripts,
106
106
  'dependencies': uiDependencies,
107
107
  'devDependencies': jsUiDevDependencies,
108
- 'jest': {
109
- roots: ['<rootDir>/src'],
110
- collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
111
- coverageThreshold: {
112
- global: {
113
- branches: 60,
114
- functions: 60,
115
- lines: 60,
116
- statements: 60,
117
- },
118
- },
119
- setupFilesAfterEnv: ['<rootDir>/config/jest/setupTests.js'],
120
- globalSetup: '<rootDir>/config/jest/globalSetup.js',
121
- testMatch: ['<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}', '<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}'],
122
- testEnvironment: 'jsdom',
123
- transform: {
124
- '^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': '<rootDir>/config/jest/babelTransform.js',
125
- '^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
126
- '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': '<rootDir>/config/jest/fileTransform.js',
127
- },
128
- transformIgnorePatterns: [
129
- '/node_modules/(?!.*@babel/runtime|@icon-park/react/es).+\\.(js|jsx|mjs|cjs|ts|tsx)$',
130
- '^.+\\.(css|sass|scss)$',
131
- ],
132
- modulePaths: [],
133
- moduleNameMapper: {
134
- '~(.*)': '<rootDir>/src/$1',
135
- '^.+\\.(css|sass|scss)$': 'identity-obj-proxy',
136
- '@corva/ui(.*)': '@corva/ui/cjs-bundle/$1',
137
- },
138
- watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
139
- resetMocks: true,
140
- },
141
108
  'lint-staged': {
142
109
  'src/**/*.{js,jsx,ts,tsx}': 'eslint --fix',
143
110
  },
144
111
  'resolutions': {
145
112
  '@noble/hashes': '1.8.0',
113
+ 'lodash': '4.18.1',
146
114
  },
147
115
  };
148
116
 
149
117
  const tsUiPackage = {
150
118
  ...uiPackage,
119
+ scripts: { ...uiScripts, typecheck: 'tsc --noEmit' },
151
120
  devDependencies: tsUiDevDependencies,
152
121
  // todo: temporary solution, ref https://github.com/oppia/oppia/issues/22283#issuecomment-2756641371
153
122
  resolutions: {
@@ -165,16 +134,19 @@ const nodeNpmScripts = {
165
134
  };
166
135
 
167
136
  const nodeDependencies = {
168
- '@corva/node-sdk': '^8.3.0',
137
+ '@corva/node-sdk': '^8.5.0',
169
138
  };
170
139
 
171
140
  const nodeDevDependencies = {
172
- '@corva/eslint-config-node': '^5.1.1',
141
+ '@corva/eslint-config-node': '^6.0.0',
142
+ '@typescript-eslint/eslint-plugin': '^8.58.1',
143
+ '@typescript-eslint/parser': '^8.58.1',
173
144
  'jest': '^29.7.0',
174
145
  'dotenv': '^16.0.3',
175
146
  'eslint': '^8.2.0',
147
+ 'eslint-plugin-jest': '^27.1.5',
176
148
  'prettier': '^2.0.0',
177
- 'typescript': '^5.5.4',
149
+ 'typescript': '^5.9.3',
178
150
  };
179
151
 
180
152
  const nodeTsDevDependencies = {
@@ -219,12 +191,47 @@ const nodeNpmPackage = {
219
191
  eslintConfig: {
220
192
  root: true,
221
193
  extends: '@corva/eslint-config-node',
194
+ overrides: [
195
+ {
196
+ files: ['**/*.ts'],
197
+ parser: '@typescript-eslint/parser',
198
+ parserOptions: {
199
+ project: 'tsconfig.json',
200
+ sourceType: 'module',
201
+ },
202
+ plugins: ['@typescript-eslint/eslint-plugin'],
203
+ rules: {
204
+ '@typescript-eslint/padding-line-between-statements': 'off',
205
+ },
206
+ },
207
+ {
208
+ files: [
209
+ '**/*.spec.js',
210
+ '**/*.spec.ts',
211
+ '**/*.spec.mjs',
212
+ '**/*.spec.cjs',
213
+ '**/__mocks__/**/*.js',
214
+ '**/__mocks__/**/*.ts',
215
+ '**/__mocks__/**/*.cjs',
216
+ '**/__mocks__/**/*.mjs',
217
+ ],
218
+ plugins: ['jest'],
219
+ env: {
220
+ jest: true,
221
+ },
222
+ },
223
+ ],
224
+ },
225
+ overrides: {
226
+ lodash: '4.18.1',
222
227
  },
223
228
  };
224
229
 
225
230
  const nodeYarnPackage = {
226
231
  ...nodeNpmPackage,
227
232
  scripts: nodeYarnScripts,
233
+ overrides: undefined,
234
+ resolutions: nodeNpmPackage.overrides,
228
235
  };
229
236
 
230
237
  const nodeTsNpmPackage = {
@@ -241,11 +248,14 @@ const nodeTsNpmPackage = {
241
248
  const nodeTsYarnPackage = {
242
249
  ...nodeTsNpmPackage,
243
250
  scripts: nodeTsYarnScripts,
251
+ overrides: undefined,
252
+ resolutions: nodeNpmPackage.overrides,
244
253
  };
245
254
 
246
255
  const extendWithTsConfig = (packageJson, version) => {
247
256
  packageJson.devDependencies = {
248
- [`@tsconfig/node${version}`]: `^${version === '12' ? '1.0.9' : '1.0.1'}`,
257
+ // New @tsconfig/nodeX package versioning: it starts with X major version
258
+ [`@tsconfig/node${version}`]: `^${version}`,
249
259
  ...packageJson.devDependencies,
250
260
  };
251
261
 
package/lib/flow.js CHANGED
@@ -29,6 +29,12 @@ export const runFlow = async (flow, context, indent = '') => {
29
29
 
30
30
  const runSteps = async (steps = [], context = {}, indent = '') => {
31
31
  for (const step of steps) {
32
+ if (typeof step.skip === 'function' && step.skip(context)) {
33
+ debug('Skipping %s', step.name || step.message);
34
+
35
+ continue;
36
+ }
37
+
32
38
  if (step.name) {
33
39
  const result = await runFlow(step, context, indent);
34
40
 
@@ -11,7 +11,7 @@ const SCHEDULER_MAPPING = [SCHEDULER_TYPE_DATA_TIME, SCHEDULER_TYPE_DEPTH, SCHED
11
11
  {},
12
12
  );
13
13
 
14
- const NODE_RUNTIMES = [APP_RUNTIMES.NODE18, APP_RUNTIMES.NODE20, APP_RUNTIMES.NODE22, APP_RUNTIMES.NODE24];
14
+ const NODE_RUNTIMES = [APP_RUNTIMES.NODE24];
15
15
 
16
16
  export class Manifest {
17
17
  constructor(manifest) {
@@ -8,6 +8,7 @@ import { ADD_LABEL_STEP } from './steps/release/add-label.js';
8
8
  import { ADD_NOTES_STEP } from './steps/release/add-notes.js';
9
9
  import { PUBLISH_PACKAGE_STEP } from './steps/release/publish.js';
10
10
  import { RELEASE_PREPARE_DATA_STEP } from './steps/release/prepare-data.js';
11
+ import { RESOLVE_PREBUILT_ZIP_STEP } from './steps/release/resolve-prebuilt-zip.js';
11
12
 
12
13
  export const RELEASE_FLOW = {
13
14
  name: 'release',
@@ -15,6 +16,9 @@ export const RELEASE_FLOW = {
15
16
  PREPARE_FLOW,
16
17
  SETUP_API_CLIENT_STEP,
17
18
  RELEASE_PREPARE_DATA_STEP,
19
+ // With --zip-file-name the archive already exists, so the zip flow is skipped entirely. That
20
+ // also lets runtimes the zip flow has no file list for (e.g. `provided.*`) be released.
21
+ RESOLVE_PREBUILT_ZIP_STEP,
18
22
  ZIP_SIMPLE_FLOW,
19
23
  UPLOAD_ZIP_TO_CORVA_STEP,
20
24
  WAIT_FOR_BUILD_FINISH_STEP,
@@ -0,0 +1,35 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ import { StepError } from '../../lib/step-error.js';
5
+
6
+ /**
7
+ * Handles `--zip-file-name`: the caller has already built the package archive, so this records it
8
+ * as the archive to upload and marks the zip flow as a no-op.
9
+ */
10
+ export const RESOLVE_PREBUILT_ZIP_STEP = {
11
+ message: 'Resolving prebuilt archive...',
12
+ fn: async ({ dirName, options }) => {
13
+ const { zipFileName } = options;
14
+
15
+ if (!zipFileName) {
16
+ return {};
17
+ }
18
+
19
+ const zipPath = resolve(dirName, zipFileName);
20
+
21
+ try {
22
+ await fs.access(zipPath);
23
+ } catch {
24
+ throw new StepError(`Prebuilt archive ${zipFileName} not found in ${dirName}`);
25
+ }
26
+
27
+ return {
28
+ zipFileName,
29
+ skipZip: true,
30
+ // The archive belongs to the caller. Neither flag reaches the context today; this is defensive.
31
+ removeOnSuccess: false,
32
+ removeOnFail: false,
33
+ };
34
+ },
35
+ };
@@ -110,7 +110,8 @@ const resolveDataToZipUiApp = async (itemsToZip = [], { options, pkg, dirName, m
110
110
 
111
111
  itemsToZip.push(
112
112
  'manifest.json',
113
- 'config-overrides.js',
113
+ 'vite.config.mjs',
114
+ 'index.html',
114
115
  'eslint.config.js',
115
116
  'eslint.config.mjs',
116
117
  'tsconfig.json',
@@ -255,7 +256,7 @@ const resolveDataForZipPythonApp = async (itemsToZip = [], { manifest, dirName,
255
256
  );
256
257
  }
257
258
 
258
- itemsToZip.push('manifest.json', 'requirements.txt');
259
+ itemsToZip.push('manifest.json', 'pyproject.toml', 'uv.lock', 'requirements.txt');
259
260
 
260
261
  return {
261
262
  zipFileName,
@@ -2,5 +2,7 @@ import { ZIP_STEPS } from './steps/zip.js';
2
2
 
3
3
  export const ZIP_SIMPLE_FLOW = {
4
4
  name: 'zip (simple)',
5
+ // Set by RESOLVE_PREBUILT_ZIP_STEP when --zip-file-name names an existing archive.
6
+ skip: ({ skipZip }) => Boolean(skipZip),
5
7
  steps: ZIP_STEPS,
6
8
  };
@@ -75,7 +75,20 @@ export async function warnIfOutdated() {
75
75
  logger.write('Checking for updates...\n');
76
76
 
77
77
  const currentVersion = await getCurrentVersion();
78
- const latestVersion = await getLatestVersion();
78
+
79
+ let latestVersion;
80
+
81
+ try {
82
+ latestVersion = await getLatestVersion();
83
+ } catch (caughtError) {
84
+ if (!(caughtError instanceof StepError)) {
85
+ throw caughtError;
86
+ }
87
+
88
+ logger.write(' ⚠️ Skipping version check (could not reach npm registry).\n');
89
+
90
+ return;
91
+ }
79
92
 
80
93
  // Skip version check if current version is a prerelease (dev or next)
81
94
  if (!isRegularVersion(currentVersion)) {
@@ -6,6 +6,8 @@ import os from 'os';
6
6
  import semver from 'semver';
7
7
 
8
8
  const debug = debugFn('cca:resolve-app-runtime');
9
+ const DEFAULT_UI_NODE_VERSION = '24';
10
+ const DEFAULT_UI_NODE_RANGE = '>=24 <25';
9
11
 
10
12
  /**
11
13
  *
@@ -37,11 +39,14 @@ const checkCliVersion = async (command, version) =>
37
39
 
38
40
  debug(`%s version output: %s`, command, data.toString());
39
41
 
40
- resolve(semver.satisfies(semver.coerce(data.toString('utf-8')), version));
42
+ const currentVersion = semver.coerce(data.toString('utf-8'));
43
+ const requiredVersion = semver.coerce(semverVersionsMapping.node[version] || version);
44
+
45
+ resolve(semver.gte(currentVersion, requiredVersion));
41
46
  });
42
47
  });
43
48
 
44
- const checkNodeVersion = (version) => async () => {
49
+ const checkNodeVersion = (range) => async () => {
45
50
  try {
46
51
  await fs.access(`${os.homedir()}/.nvm/nvm.sh`);
47
52
 
@@ -52,7 +57,7 @@ const checkNodeVersion = (version) => async () => {
52
57
  debug(e);
53
58
  debug('nvm is not installed, checking node version');
54
59
 
55
- return checkCliVersion('node', version);
60
+ return checkCliVersion('node', range);
56
61
  }
57
62
  };
58
63
 
@@ -60,19 +65,22 @@ export const IS_WINDOWS = process.platform === 'win32';
60
65
 
61
66
  const semverVersionsMapping = {
62
67
  node: {
63
- 18: '18.14.0',
64
- 20: '20.16.0',
65
- 24: '24.14.0',
68
+ 18: '18.20.8',
69
+ 20: '20.20.1',
70
+ 24: '24.15.0',
66
71
  },
67
72
  python: {
68
- '3.8': '3.8.16',
69
- '3.9': '3.9.16',
70
- '3.10': '3.10.9',
71
- '3.11': '3.11.1',
72
- '3.13': '3.13.7',
73
+ 3.13: '3.13.13',
74
+ 3.14: '3.14.4',
73
75
  },
74
76
  };
75
77
 
78
+ const nodeVersionRanges = {
79
+ 18: '>=18 <19',
80
+ 20: '>=20 <21',
81
+ 24: '>=24 <25',
82
+ };
83
+
76
84
  /**
77
85
  * @typedef {Object} Runtime
78
86
  * @property {string} language
@@ -80,18 +88,20 @@ const semverVersionsMapping = {
80
88
  * @property {string} packageManager
81
89
  * @property {string} version
82
90
  * @property {string} semver
91
+ * @property {string} [enginesNode]
83
92
  */
84
93
  export const resolveAppRuntime = (opts) => {
85
94
  if (opts.appType === APP_TYPES.UI) {
86
- const version = '20';
95
+ const version = DEFAULT_UI_NODE_VERSION;
87
96
 
88
97
  return {
89
98
  environment: 'node',
90
99
  language: opts.useTypescript ? 'typescript' : 'javascript',
91
- isRuntimeAvailable: checkNodeVersion(version),
100
+ isRuntimeAvailable: checkNodeVersion(DEFAULT_UI_NODE_RANGE),
92
101
  packageManager: opts.packageManager,
93
102
  version,
94
103
  semver: semverVersionsMapping.node[version],
104
+ enginesNode: DEFAULT_UI_NODE_RANGE,
95
105
  };
96
106
  }
97
107
 
@@ -101,10 +111,11 @@ export const resolveAppRuntime = (opts) => {
101
111
  return {
102
112
  environment: 'node',
103
113
  language: opts.useTypescript ? 'typescript' : 'javascript',
104
- isRuntimeAvailable: checkNodeVersion(version),
114
+ isRuntimeAvailable: checkNodeVersion(nodeVersionRanges[version]),
105
115
  packageManager: opts.packageManager,
106
116
  version,
107
117
  semver: semverVersionsMapping.node[version],
118
+ enginesNode: `^${version}`,
108
119
  };
109
120
  }
110
121
 
@@ -7,7 +7,7 @@ export const addUiAppFile = (templateFolder, root, runtime, manifest, opts) => {
7
7
  try {
8
8
  logger.log(chalk.green('adding app file'));
9
9
 
10
- const fileExtension = runtime.language === 'typescript' ? 'tsx' : 'js';
10
+ const fileExtension = runtime.language === 'typescript' ? 'tsx' : 'jsx';
11
11
 
12
12
  const segment =
13
13
  {
@@ -26,9 +26,13 @@ export const addUiAppFile = (templateFolder, root, runtime, manifest, opts) => {
26
26
  };
27
27
 
28
28
  export const getExcludedFiles = (manifest) => {
29
- if (manifest.isUi()) return ['App.drilling.js', 'App.completion.js', 'App.drilling.tsx', 'App.completion.tsx'];
29
+ const excludedFiles = ['.ruff_cache', '.venv', '.pytest_cache', '__pycache__'];
30
30
 
31
- return [];
31
+ if (manifest.isUi()) {
32
+ return [...excludedFiles, 'App.drilling.jsx', 'App.completion.jsx', 'App.drilling.tsx', 'App.completion.tsx'];
33
+ }
34
+
35
+ return excludedFiles;
32
36
  };
33
37
 
34
38
  export function copyFileSync(source, target) {
package/lib/main.js CHANGED
@@ -17,17 +17,16 @@ import { StepError } from './flows/lib/step-error.js';
17
17
  function checkNodeVersion() {
18
18
  logger.write('Checking node version...');
19
19
 
20
- const unsupportedNodeVersion = !semver.satisfies(process.version, '>=16');
20
+ const unsupportedNodeVersion = !semver.satisfies(process.version, '>=24 <25');
21
21
 
22
22
  if (unsupportedNodeVersion) {
23
23
  logger.log(
24
24
  chalk.red(
25
25
  `\nYou are using Node ${process.version}.\n\n` +
26
- `Please update to Node 16 or higher for a better, fully supported experience.\n`,
26
+ `Please update to Node 24.x for a better, fully supported experience.\n`,
27
27
  ),
28
28
  );
29
29
 
30
- // Fall back to latest supported react-scripts on Node 4
31
30
  return process.exit(1);
32
31
  }
33
32
 
@@ -0,0 +1,18 @@
1
+ import { InvalidArgumentError, Option } from 'commander';
2
+
3
+ const flags = '--zip-file-name <string>';
4
+ const description =
5
+ 'Upload an already-built .zip from the project directory instead of zipping it here. ' +
6
+ 'The archive belongs to the caller and is never deleted after upload.';
7
+
8
+ // An unset CI variable arrives as `--zip-file-name=`, and commander 9 takes the next token as the
9
+ // value even when it is another flag — neither should pass for an archive name.
10
+ function argParser(value) {
11
+ if (typeof value !== 'string' || !value.trim() || value.startsWith('-')) {
12
+ throw new InvalidArgumentError('Expected a file name, e.g. --zip-file-name=package.zip.');
13
+ }
14
+
15
+ return value;
16
+ }
17
+
18
+ export const zipFileNameOption = new Option(flags, description).argParser(argParser);
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@corva/create-app","version":"0.0.0-2576df8","private":false,"description":"Create an app to use it in CORVA.AI","keywords":["react"],"bugs":{"url":"https://github.com/facebook/create-react-app/issues"},"repository":{"type":"git","url":"https://github.com/corva-ai/create-corva-app","directory":"@corva/create-app"},"license":"MIT","type":"module","bin":{"create-corva-app":"./bin/create-corva-app.cjs"},"files":["bin/**/*.cjs","bin/**/*.js","lib/**/*.js","templates","template_extensions","common"],"scripts":{"build":"echo \"build is not configured for package \\\"×\\\", skipping.\"","get-changelog":"conventional-changelog -r 2 -p angular","lint":"eslint . --resolve-plugins-relative-to $(pwd) --max-warnings 0","lint:fix":"eslint . --fix","release":"git add -A && standard-version -a","release-rc":"npm run release -- --prerelease","run:local-ui-ts-completion":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-completions --appName 'TestAppUiTsCompletions' --segments 'completion' --category 'analytics' --appKey 'corva.testappuitscompletions.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","run:local-ui-ts-drilling":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-drilling --appName 'TestAppUiTsDrilling' --segments 'drilling' --category 'analytics' --appKey 'corva.testappuitsdrilling.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","run:local-ui-ts-intervention":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-intervention --appName 'TestAppUiTsIntervention' --segments 'intervention' --category 'analytics' --appKey 'corva.testappuitsintervention.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","test":"NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"},"lint-staged":{"*.js":"npm run lint:fix"},"dependencies":{"archiver":"^5.3.0","chalk":"^4.1.0","commander":"^9.1.0","cross-spawn":"^7.0.3","debug":"^4.3.4","dotenv":"^16.0.0","figlet":"^1.5.0","form-data":"^4.0.0","fs-extra":"^9.0.1","glob":"^8.0.1","got":"^12.5.1","inquirer":"^8.2.3","is-glob":"^4.0.3","lodash":"^4.17.21","npm-api":"^1.0.0","semver":"^7.3.2","terminal-link":"^3.0.0"},"devDependencies":{"@babel/core":"^7.11.0","@babel/eslint-parser":"^7.19.1","@babel/plugin-proposal-class-properties":"^7.18.6","@babel/plugin-syntax-import-assertions":"^7.20.0","@babel/preset-react":"^7.18.6","@commitlint/cli":"^17.3.0","@commitlint/config-conventional":"^17.3.0","@corva/eslint-config-browser":"^0.1.7","@corva/eslint-config-node":"^5.1.1","@corva/node-sdk":"^8.0.1","@types/cross-spawn":"^6.0.2","@types/jest":"^29.5.4","@typescript-eslint/eslint-plugin":"^5.42.1","@typescript-eslint/parser":"^5.42.1","conventional-changelog-cli":"^2.1.0","eslint":"^8.2.0","eslint-config-google":"^0.14.0","eslint-config-prettier":"^8.5.0","eslint-plugin-import":"^2.26.0","eslint-plugin-jest":"^27.1.5","eslint-plugin-jsx-a11y":"^6.7.1","eslint-plugin-prettier":"^4.2.1","eslint-plugin-react":"^7.32.0","eslint-plugin-react-hooks":"^4.6.0","eslint-plugin-require-sort":"^1.3.0","husky":"^8.0.2","jest":"^29.6.4","prettier":"^2.0.0","prettier-plugin-packagejson":"^2.3.0","standard-version":"^9.0.0","typescript":"^4.8.4","zx":"^7.2.3"},"packageManager":"yarn@1.22.19+sha512.ff4579ab459bb25aa7c0ff75b62acebe576f6084b36aa842971cf250a5d8c6cd3bc9420b22ce63c7f93a0857bc6ef29291db39c3e7a23aab5adfd5a4dd6c5d71","engines":{"node":">=18"},"standard-version":{"skip":{"tag":true}}}
1
+ {"name":"@corva/create-app","version":"0.0.0-5fb771a","private":false,"description":"Create an app to use it in CORVA.AI","keywords":["react"],"bugs":{"url":"https://github.com/facebook/create-react-app/issues"},"repository":{"type":"git","url":"https://github.com/corva-ai/create-corva-app"},"license":"MIT","type":"module","bin":{"create-corva-app":"./bin/create-corva-app.cjs"},"files":["bin/**/*.cjs","bin/**/*.js","lib/**/*.js","templates","template_extensions","common"],"scripts":{"build":"echo \"build is not configured for package \\\"×\\\", skipping.\"","get-changelog":"conventional-changelog -r 2 -p angular","lint":"eslint . --resolve-plugins-relative-to $(pwd) --max-warnings 0","lint:fix":"eslint . --fix","release":"git add -A && standard-version -a","release-rc":"npm run release -- --prerelease","run:local-ui-ts-completion":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-completions --appName 'TestAppUiTsCompletions' --segments 'completion' --category 'analytics' --appKey 'corva.testappuitscompletions.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","run:local-ui-ts-drilling":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-drilling --appName 'TestAppUiTsDrilling' --segments 'drilling' --category 'analytics' --appKey 'corva.testappuitsdrilling.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","run:local-ui-ts-intervention":"node ./bin/create-corva-app.cjs ../test-app-ui-ts-intervention --appName 'TestAppUiTsIntervention' --segments 'intervention' --category 'analytics' --appKey 'corva.testappuitsintervention.ui' --appType 'ui' --runtime 'ui' -t --extensions corva","test":"NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"},"lint-staged":{"*.js":"npm run lint:fix"},"resolutions":{"lodash":"4.18.1"},"dependencies":{"archiver":"^5.3.0","chalk":"^4.1.0","commander":"^9.1.0","cross-spawn":"^7.0.3","debug":"^4.3.4","dotenv":"^16.0.0","figlet":"^1.5.0","form-data":"^4.0.0","fs-extra":"^9.0.1","glob":"^8.0.1","got":"^12.5.1","inquirer":"^8.2.3","is-glob":"^4.0.3","lodash":"^4.18.1","npm-api":"^1.0.0","semver":"^7.3.2","terminal-link":"^3.0.0"},"devDependencies":{"@babel/core":"^7.11.0","@babel/eslint-parser":"^7.19.1","@babel/plugin-proposal-class-properties":"^7.18.6","@babel/plugin-syntax-import-assertions":"^7.20.0","@babel/preset-react":"^7.18.6","@commitlint/cli":"^17.3.0","@commitlint/config-conventional":"^17.3.0","@corva/eslint-config-browser":"^0.1.7","@corva/eslint-config-node":"^6.0.0","@corva/node-sdk":"^8.5.0","@types/cross-spawn":"^6.0.2","@types/jest":"^29.5.4","@typescript-eslint/eslint-plugin":"^8.58.1","@typescript-eslint/parser":"^8.58.1","conventional-changelog-cli":"^2.1.0","eslint":"^8.2.0","eslint-config-google":"^0.14.0","eslint-config-prettier":"^8.5.0","eslint-plugin-import":"^2.26.0","eslint-plugin-jest":"^27.1.5","eslint-plugin-jsx-a11y":"^6.7.1","eslint-plugin-prettier":"^4.2.1","eslint-plugin-react":"^7.32.0","eslint-plugin-react-hooks":"^4.6.0","eslint-plugin-require-sort":"^1.3.0","husky":"^8.0.2","jest":"^29.6.4","prettier":"^2.0.0","prettier-plugin-packagejson":"^2.3.0","standard-version":"^9.0.0","typescript":"^5.9.3","zx":"^7.2.3"},"packageManager":"yarn@1.22.19+sha512.ff4579ab459bb25aa7c0ff75b62acebe576f6084b36aa842971cf250a5d8c6cd3bc9420b22ce63c7f93a0857bc6ef29291db39c3e7a23aab5adfd5a4dd6c5d71","standard-version":{"skip":{"tag":true}}}
@@ -4,8 +4,7 @@
4
4
  "parserOptions": {
5
5
  "requireConfigFile": false,
6
6
  "babelOptions": {
7
- "presets": ["@babel/preset-react"],
8
- "plugins": ["@babel/plugin-proposal-class-properties"]
7
+ "presets": ["@babel/preset-react"]
9
8
  }
10
9
  }
11
10
  }
@@ -10,22 +10,24 @@ These constraints are enforced by the platform. Violating them breaks the build
10
10
  1. **Root export** — `src/index.js` MUST default-export `{ component: App, settings: AppSettings }`. The component may also be a `ParentApp` wrapper (e.g., `{ component: ParentApp, settings: AppSettings }`) that wraps `App` with Context.Providers and prop initialization. This is the platform loader contract.
11
11
  2. **UI components** — Use only `@corva/ui` components. Import from `@corva/ui/componentsV2`; fall back to `@corva/ui/components` only when a V2 version doesn't exist yet. `@material-ui/core` v4 is available as the underlying library.
12
12
  3. **HTTP clients** — Use `corvaDataAPI` / `corvaAPI` from `@corva/ui/clients` for all network requests.
13
- 4. **Do not rename or delete** `App.js`, `AppSettings.js`, or `index.js` — they are platform entry points.
13
+ 4. **Do not rename or delete** `App.jsx`, `AppSettings.jsx`, or `index.js` — they are platform entry points.
14
14
 
15
15
  ## Project Structure
16
16
 
17
17
  ```
18
18
  src/
19
- App.js — Main app component (default export required)
20
- AppSettings.js — Settings panel component (default export required)
19
+ App.jsx — Main app component (default export required)
20
+ AppSettings.jsx — Settings panel component (default export required)
21
21
  index.js — Root export: { component, settings }
22
22
  constants.js — Default settings values
23
- App.scss — Component styles
24
- __tests__/ — Jest test files
23
+ App.module.scss — Component styles
24
+ __tests__/ — Vitest test files
25
25
  __mocks__/ — Mock data (mockAppProps.js, mockAppSettingsProps.js)
26
26
  assets/ — Static assets (SVGs, images)
27
- config/jest/ — Jest setup (setupTests, transforms)
28
- config-overrides.js — Webpack 5 customization
27
+ config/vite/ — Shared Corva Vite config
28
+ vite.config.mjs — Vite entry config
29
+ vitest.setup.js — Test environment setup
30
+ index.html — Dev server host page
29
31
  manifest.json — Corva platform app metadata (generated at scaffold time)
30
32
  ```
31
33
 
@@ -98,7 +100,7 @@ transition: transition(color, background-color);
98
100
  **Import in component:**
99
101
 
100
102
  ```javascript
101
- import styles from './App.scss';
103
+ import styles from './App.module.scss';
102
104
  // Use: <div className={styles.container}>
103
105
  ```
104
106
 
@@ -265,11 +267,11 @@ General rules:
265
267
 
266
268
  ## Testing
267
269
 
268
- - **Framework:** Jest + React Testing Library
270
+ - **Framework:** Vitest + React Testing Library
269
271
  - **Wrapper:** Use `AppTestWrapper` from `@corva/ui/testing` to wrap components
270
- - **Mocking:** Mock network requests (jest mocks or msw), never call real APIs
272
+ - **Mocking:** Mock network requests (`vi.mock`/`vi.fn` or msw), never call real APIs
271
273
  - **Timezone:** UTC is enforced (`process.env.TZ = 'UTC'`)
272
- - **Mocked globals:** `ResizeObserver`, `MutationObserver` (in `setupTests.js`)
274
+ - **Mocked globals:** `ResizeObserver`, `MutationObserver` (in `vitest.setup.js`)
273
275
 
274
276
  ## Commands
275
277
 
@@ -4,7 +4,7 @@ import { useAppCommons } from '@corva/ui/effects';
4
4
  import { DEFAULT_SETTINGS } from './constants';
5
5
  import logo from './assets/logo.svg';
6
6
 
7
- import styles from './App.scss';
7
+ import styles from './App.module.scss';
8
8
 
9
9
  const App = () => {
10
10
  const { appKey, fracFleet, well, wells, appSettings } = useAppCommons();
@@ -19,7 +19,7 @@ const App = () => {
19
19
  <div className={styles.container}>
20
20
  <img src={logo} alt="logo" className={styles.logo} />
21
21
  <p>
22
- Edit <code>src/App.js</code> and save to reload.
22
+ Edit <code>src/App.jsx</code> and save to reload.
23
23
  <br />
24
24
  <br />
25
25
  </p>
@@ -47,6 +47,6 @@ const App = () => {
47
47
  );
48
48
  };
49
49
 
50
- // Important: Do not change root component default export (App.js). Use it as container
50
+ // Important: Do not change root component default export (App.jsx). Use it as container
51
51
  // for your App. It's required to make build and zip scripts work as expected;
52
52
  export default App;