@marsx-dev/launcher 0.0.2 → 0.0.5

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 (54) hide show
  1. package/dist/cli/index.d.ts +4 -0
  2. package/dist/cli/index.d.ts.map +1 -0
  3. package/dist/cli/index.js +1 -0
  4. package/dist/cli/init.d.ts +2 -0
  5. package/dist/cli/init.d.ts.map +1 -0
  6. package/dist/cli/init.js +90 -59
  7. package/dist/cli/migrate.d.ts +2 -0
  8. package/dist/cli/migrate.d.ts.map +1 -0
  9. package/dist/cli/migrate.js +3 -2
  10. package/dist/cli/start.d.ts +3 -0
  11. package/dist/cli/start.d.ts.map +1 -0
  12. package/dist/cli/start.js +3 -2
  13. package/dist/configuration.d.ts +30 -0
  14. package/dist/configuration.d.ts.map +1 -0
  15. package/dist/configuration.js +95 -33
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +40 -5
  19. package/dist/launcher.d.ts +2 -0
  20. package/dist/launcher.d.ts.map +1 -0
  21. package/dist/launcher.js +6 -4
  22. package/dist/loader.d.ts +3 -0
  23. package/dist/loader.d.ts.map +1 -0
  24. package/dist/loader.js +5 -4
  25. package/dist/utils/compile.d.ts +9 -0
  26. package/dist/utils/compile.d.ts.map +1 -0
  27. package/dist/utils/compile.js +8 -6
  28. package/dist/utils/fileUtils.d.ts +12 -0
  29. package/dist/utils/fileUtils.d.ts.map +1 -0
  30. package/dist/utils/fileUtils.js +42 -0
  31. package/dist/utils/sfc.d.ts +55 -0
  32. package/dist/utils/sfc.d.ts.map +1 -0
  33. package/dist/utils/sfc.js +54 -97
  34. package/dist/utils/textUtils.d.ts +17 -0
  35. package/dist/utils/textUtils.d.ts.map +1 -0
  36. package/dist/utils/textUtils.js +105 -0
  37. package/dist/utils/v3.d.ts +12 -0
  38. package/dist/utils/v3.d.ts.map +1 -0
  39. package/dist/utils/v3.js +5 -5
  40. package/package.json +12 -51
  41. package/src/cli/index.ts +37 -0
  42. package/src/cli/init.ts +142 -0
  43. package/src/cli/migrate.ts +23 -0
  44. package/src/cli/start.ts +34 -0
  45. package/src/configuration.ts +104 -0
  46. package/src/index.ts +6 -0
  47. package/src/launcher.ts +39 -0
  48. package/src/loader.ts +65 -0
  49. package/src/utils/compile.ts +59 -0
  50. package/src/utils/fileUtils.ts +54 -0
  51. package/src/utils/sfc.ts +155 -0
  52. package/src/utils/textUtils.ts +82 -0
  53. package/src/utils/v3.ts +104 -0
  54. package/dist/utils/utils.js +0 -59
package/dist/utils/v3.js CHANGED
@@ -42,12 +42,11 @@ const SFC_FIELD_MAP = {
42
42
  };
43
43
  function convertV3ToSfc(block) {
44
44
  const sfc = {
45
- identity: {
45
+ path: {
46
46
  folder: block.Folder,
47
47
  name: block.Name,
48
48
  blockTypeName: block.Type,
49
49
  ext: 'vue',
50
- fullName: '',
51
50
  filePath: '',
52
51
  },
53
52
  metadata: {},
@@ -83,9 +82,9 @@ function convertV3ToSfc(block) {
83
82
  exports.convertV3ToSfc = convertV3ToSfc;
84
83
  function convertSfcToV3(sfc) {
85
84
  const block = {
86
- Name: sfc.identity.name,
87
- Type: sfc.identity.blockTypeName,
88
- Folder: sfc.identity.folder,
85
+ Name: sfc.path.name,
86
+ Type: sfc.path.blockTypeName,
87
+ Folder: sfc.path.folder,
89
88
  ...sfc.metadata,
90
89
  ...sfc.jsons,
91
90
  };
@@ -95,3 +94,4 @@ function convertSfcToV3(sfc) {
95
94
  return block;
96
95
  }
97
96
  exports.convertSfcToV3 = convertSfcToV3;
97
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidjMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvdjMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBU0EsTUFBTSxhQUFhLEdBQTJCO0lBQzVDLFFBQVE7SUFDUixRQUFRLEVBQUUsTUFBTTtJQUNoQixJQUFJLEVBQUUsTUFBTTtJQUNaLEtBQUssRUFBRSxNQUFNO0lBQ2IsTUFBTSxFQUFFLE1BQU07SUFDZCxNQUFNLEVBQUUsTUFBTTtJQUNkLEtBQUssRUFBRSxNQUFNO0lBQ2IsVUFBVTtJQUNWLGFBQWEsRUFBRSxJQUFJO0lBQ25CLElBQUksRUFBRSxNQUFNO0lBQ1osSUFBSSxFQUFFLE1BQU07SUFDWixHQUFHLEVBQUUsS0FBSztJQUNWLGFBQWEsRUFBRSxJQUFJO0lBQ25CLHVCQUF1QixFQUFFLElBQUk7SUFDN0IsR0FBRyxFQUFFLEtBQUs7SUFDVixhQUFhLEVBQUUsSUFBSTtJQUNuQixPQUFPLEVBQUUsS0FBSztJQUNkLGlCQUFpQixFQUFFLElBQUk7SUFDdkIsTUFBTSxFQUFFLElBQUk7SUFDWixHQUFHLEVBQUUsS0FBSztJQUNWLFFBQVEsRUFBRSxJQUFJO0lBQ2QsY0FBYyxFQUFFLElBQUk7SUFDcEIscUJBQXFCLEVBQUUsSUFBSTtJQUMzQiw2QkFBNkI7SUFDN0IsaUJBQWlCO0lBQ2pCLElBQUksRUFBRSxRQUFRO0lBQ2QsSUFBSSxFQUFFLFFBQVE7SUFDZCxNQUFNLEVBQUUsUUFBUTtJQUNoQixrQ0FBa0M7SUFDbEMsR0FBRyxFQUFFLFFBQVE7SUFDYixPQUFPLEVBQUUsUUFBUTtJQUNqQixXQUFXLEVBQUUsUUFBUTtJQUNyQixTQUFTLEVBQUUsUUFBUTtJQUNuQixTQUFTLEVBQUUsUUFBUTtJQUNuQixTQUFTLEVBQUUsUUFBUTtJQUNuQixTQUFTLEVBQUUsUUFBUTtJQUNuQixtREFBbUQ7Q0FDcEQsQ0FBQztBQUVGLFNBQWdCLGNBQWMsQ0FBQyxLQUFtQjtJQUNoRCxNQUFNLEdBQUcsR0FBYTtR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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marsx-dev/launcher",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "description": "MarsX launcher",
5
5
  "author": "MarsX team <team@marsx.dev>",
6
6
  "homepage": "https://marsx.dev/",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "files": [
21
21
  "dist",
22
+ "src",
22
23
  "LICENSE",
23
24
  "README.md"
24
25
  ],
@@ -39,67 +40,27 @@
39
40
  },
40
41
  "dependencies": {
41
42
  "@vue/compiler-sfc": "^3.2.29",
42
- "axios": "^0.26.1",
43
43
  "chalk": "^4.1.2",
44
44
  "commander": "^9.1.0",
45
- "config": "^3.3.7",
46
45
  "json-stable-stringify": "^1.0.1",
47
46
  "json5": "^2.2.0",
47
+ "yup": "^0.32.11"
48
+ },
49
+ "peerDependencies": {
50
+ "@types/config": "^0.0.41",
51
+ "@types/lodash": "^4.14.178",
52
+ "@types/node": "^17.0.21",
53
+ "@types/prettier": "^2.4.4",
54
+ "axios": "^0.26.1",
55
+ "config": "^3.3.7",
48
56
  "lodash": "^4.17.21",
49
57
  "mongodb": "^4.3.1",
50
58
  "prettier": "^2.5.1",
51
59
  "typescript": "^4.5.5"
52
60
  },
53
- "peerDependencies": {
54
- "@fastify/session": "^6.4.0",
55
- "@stitches/core": "^1.2.6",
56
- "@types/accepts": "^1.3.5",
57
- "@types/bcrypt": "^5.0.0",
58
- "@types/mkdirp": "^1.0.2",
59
- "@types/nodegit": "^0.27.9",
60
- "@types/sharp": "^0.29.5",
61
- "@types/uuid": "^8.3.4",
62
- "@types/ws": "^8.5.2",
63
- "aws-sdk": "^2.1073.0",
64
- "azure-storage": "^2.10.7",
65
- "babel-core": "^6.26.3",
66
- "babel-plugin-transform-react-jsx": "^6.24.1",
67
- "base-x": "^4.0.0",
68
- "bcrypt": "^5.0.1",
69
- "chokidar": "^3.5.3",
70
- "dotenv": "^16.0.0",
71
- "fastify": "^3.27.2",
72
- "fastify-accepts": "^2.1.0",
73
- "fastify-compress": "^4.0.1",
74
- "fastify-cookie": "^5.6.0",
75
- "fastify-cors": "^6.0.3",
76
- "fastify-csrf": "^3.1.0",
77
- "fastify-flash": "^2.0.2",
78
- "fastify-formbody": "^5.2.0",
79
- "fastify-helmet": "^7.0.1",
80
- "fastify-multipart": "^5.3.1",
81
- "fastify-request-context": "^2.2.0",
82
- "fastify-static": "^4.5.0",
83
- "fastify-websocket": "^4.2.0",
84
- "fontverter": "^2.0.0",
85
- "jsonwebtoken": "^8.5.1",
86
- "mobile-detect": "^1.4.5",
87
- "moment-timezone": "^0.5.34",
88
- "nodegit": "^0.28.0-alpha.10",
89
- "route-pattern": "^0.0.6",
90
- "route-sort": "^1.0.0",
91
- "sharp": "^0.30.1",
92
- "source-map": "^0.7.3",
93
- "subset-font": "^1.4.0",
94
- "uuid": "^8.3.2",
95
- "xxhash": "^0.3.0"
96
- },
97
61
  "devDependencies": {
98
- "@types/config": "^0.0.41",
99
62
  "@types/json-stable-stringify": "^1.0.34",
100
- "@types/lodash": "^4.14.178",
101
- "@types/node": "^17.0.21",
102
- "@types/prettier": "^2.4.4",
63
+ "@types/yup": "^0.29.13",
103
64
  "@typescript-eslint/eslint-plugin": "^5.10.2",
104
65
  "@typescript-eslint/parser": "^5.10.2",
105
66
  "eslint": "^8.8.0",
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ import chalk from 'chalk';
4
+ import { Command } from 'commander';
5
+ import { initProject } from './init';
6
+ import { migrateV3ToV4 } from './migrate';
7
+ import { start } from './start';
8
+
9
+ export class CliError extends Error {}
10
+
11
+ async function main() {
12
+ try {
13
+ const program = new Command();
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
16
+ const version = require('../../package.json').version;
17
+ program.name('marsx').description('CLI for MarsX launcher').version(version);
18
+
19
+ program.command('start', { isDefault: true }).description('Start MarsX server').action(start);
20
+ program
21
+ .command('init <project_name>')
22
+ .description('Initialize a new MarsX project named <project_name> in a directory of the same name')
23
+ .action(initProject);
24
+ program.command('migrate').description('Migrate MarsX V3 project to V4').action(migrateV3ToV4);
25
+
26
+ await program.parseAsync();
27
+ } catch (e) {
28
+ if (e instanceof CliError) {
29
+ console.error(chalk.red(e.message));
30
+ } else {
31
+ console.error(chalk.red(e));
32
+ }
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ main();
@@ -0,0 +1,142 @@
1
+ import chalk from 'chalk';
2
+ import { spawnSync } from 'child_process';
3
+ import { randomBytes } from 'crypto';
4
+ import path from 'path';
5
+ import { Config, CustomEnvironmentVariables } from '../configuration';
6
+ import { isDirectory, writeFileMakeDir } from '../utils/fileUtils';
7
+ import { assert } from '../utils/textUtils';
8
+ import { CliError } from './index';
9
+
10
+ const FASTIFY_DEPS = [
11
+ '@fastify/session',
12
+ 'fastify',
13
+ 'fastify-accepts',
14
+ 'fastify-compress',
15
+ 'fastify-cookie',
16
+ 'fastify-cors',
17
+ 'fastify-csrf',
18
+ 'fastify-flash',
19
+ 'fastify-formbody',
20
+ 'fastify-helmet',
21
+ 'fastify-multipart',
22
+ 'fastify-request-context',
23
+ 'fastify-static',
24
+ 'fastify-websocket',
25
+ ];
26
+
27
+ const COMMON_DEPS = [
28
+ '@marsx-dev/launcher',
29
+ 'aws-sdk',
30
+ 'axios',
31
+ 'azure-storage',
32
+ 'base-x',
33
+ 'bcrypt',
34
+ 'chokidar',
35
+ 'lodash',
36
+ 'mongodb4@npm:mongodb@4',
37
+ 'mongodb3@npm:mongodb@3',
38
+ 'typescript',
39
+ 'uuid',
40
+ 'xxhash',
41
+ ];
42
+
43
+ const V3_DEPS = [
44
+ 'babel-core',
45
+ 'babel-plugin-transform-react-jsx',
46
+ 'config',
47
+ 'crypto-js',
48
+ 'jsonwebtoken',
49
+ 'mobile-detect',
50
+ 'moment-timezone',
51
+ 'route-pattern',
52
+ 'route-sort',
53
+ ];
54
+
55
+ const DEFAULT_DEPS = [...FASTIFY_DEPS, ...COMMON_DEPS, ...V3_DEPS];
56
+
57
+ export async function initProject(projectName: string) {
58
+ if (!projectName.match(/^\w+$/) || projectName !== projectName.toLowerCase()) {
59
+ throw new CliError(
60
+ `Project name "${projectName}" may contain only lower case alphanumeric characters and underscores (eg. my_project_name)`,
61
+ );
62
+ }
63
+ const name = {
64
+ snakeCase: projectName,
65
+ withDashes: projectName.replace('_', '-'),
66
+ noSep: projectName.replace('_', ''),
67
+ };
68
+
69
+ const projectDir = path.resolve(projectName);
70
+
71
+ if (await isDirectory(projectDir)) {
72
+ throw new CliError(`Project dir "${projectName}" already exist. Delete it or choose a different name.`);
73
+ }
74
+
75
+ console.log(`Creating a new MarsX project in ${chalk.green(projectDir)}`);
76
+
77
+ const packageJson = {
78
+ name: name.withDashes,
79
+ version: '0.0.0',
80
+ private: true,
81
+ scripts: {
82
+ start: 'marsx start',
83
+ },
84
+ dependencies: {},
85
+ };
86
+ const packageJsonStr = JSON.stringify(packageJson, null, 2);
87
+ await writeFileMakeDir(path.join(projectDir, 'package.json'), packageJsonStr);
88
+
89
+ const config: Config = {
90
+ production: false,
91
+ port: 3000,
92
+ blocksDir: 'blocks',
93
+ cacheDir: '.cache',
94
+ mongoConn: '<CONN_STR>',
95
+ mongoDbName: name.withDashes,
96
+ azureStorageConnection: '<CONN_STR>',
97
+ azureStorageAccountName: name.noSep,
98
+ azureStorageUrl: `https://${name.noSep}.blob.core.windows.net`,
99
+ webFilesTable: 'webFiles',
100
+ webRecentFilesTable: 'webRecentFiles',
101
+ webFilesBlob: 'web-files',
102
+ secret: (await randomBytes(32)).toString('hex'),
103
+ importProjects: [
104
+ {
105
+ name: 'marsx-core',
106
+ url: 'https://core.marsx.dev',
107
+ api_key: '<API_KEY>',
108
+ git_commit_ish: 'main',
109
+ },
110
+ ],
111
+ };
112
+
113
+ await writeFileMakeDir(path.join(projectDir, 'config', 'default.json'), JSON.stringify(config, null, 2));
114
+ await writeFileMakeDir(
115
+ path.join(projectDir, 'config', 'custom-environment-variables.json'),
116
+ JSON.stringify(CustomEnvironmentVariables, null, 2),
117
+ );
118
+ await writeFileMakeDir(path.join(projectDir, '.gitignore'), 'node_modules\ndist\n.cache\n');
119
+
120
+ function run(...args: string[]) {
121
+ const cmd = args[0];
122
+ assert(cmd);
123
+ spawnSync(cmd, args.slice(1), { cwd: projectDir, stdio: 'inherit' });
124
+ }
125
+
126
+ console.log('\nInstalling dependencies. This might take a couple of minutes.');
127
+ run('npm', 'i', ...DEFAULT_DEPS);
128
+ console.log('');
129
+
130
+ run('git', 'init', '--initial-branch=main');
131
+ run('git', 'add', '.gitignore');
132
+ run('git', 'add', '-A');
133
+ run('git', 'commit', '-m', 'Initial MarsX commit');
134
+
135
+ console.log(`\n${chalk.green('Success!')} Created ${projectName} at ${projectDir}`);
136
+
137
+ console.log(`\nNext steps:`);
138
+ console.log(` - Open new project directory: ${chalk.underline(`cd ${projectName}`)}`);
139
+ console.log(` - Contact MarsX to get config params and update them: ${chalk.underline('code config/default.json')}`);
140
+ console.log(` - (optionally) Migrate V3 blocks using: ${chalk.underline('npx marsx migrate')}`);
141
+ console.log(` - Start local server: ${chalk.underline('npm run start')}`);
142
+ }
@@ -0,0 +1,23 @@
1
+ import chalk from 'chalk';
2
+ import { MongoClient } from 'mongodb';
3
+ import path from 'path';
4
+ import { config } from '../configuration';
5
+ import { serializeSfc } from '../utils/sfc';
6
+ import { writeFileMakeDir } from '../utils/fileUtils';
7
+ import { convertV3ToSfc, V3MongoBlock } from '../utils/v3';
8
+
9
+ export async function migrateV3ToV4() {
10
+ console.log('Connecting to MongoDB...');
11
+ const mongoConn = await new MongoClient(config.mongoConn).connect();
12
+ const db = mongoConn.db(config.mongoDbName);
13
+ const allBlocks = await db.collection<V3MongoBlock>('blocks').find().toArray();
14
+ console.log(`Downloaded ${allBlocks.length} block(s)`);
15
+
16
+ for (const block of allBlocks) {
17
+ const serialized = serializeSfc(convertV3ToSfc(block));
18
+ await writeFileMakeDir(path.join(config.blocksDir, serialized.filePath), serialized.content);
19
+ console.log(`Saved ${serialized.filePath}`);
20
+ }
21
+
22
+ console.log(chalk.green('\nMigration complete successfully!'));
23
+ }
@@ -0,0 +1,34 @@
1
+ import child_process from 'child_process';
2
+ import { config } from '../configuration';
3
+ import { launchBooter } from '../launcher';
4
+ import { assert } from '../utils/textUtils';
5
+
6
+ export const RESTART_EXIT_CODe = 9;
7
+
8
+ export const start = async () => {
9
+ if (config.production || process.env['MARSX_NO_SPAWN']) {
10
+ console.log(`MarsX process ${process.pid} starting...`);
11
+ await launchBooter();
12
+ console.log(`MarsX process ${process.pid} started`);
13
+ } else {
14
+ const spawnChildProc = () => {
15
+ assert(process.argv[0]);
16
+ const child = child_process.spawn(process.argv[0], ['--inspect', '--enable-source-maps', ...process.argv.slice(1)], {
17
+ cwd: process.cwd(),
18
+ env: { ...process.env, MARSX_NO_SPAWN: 'true' },
19
+ stdio: 'inherit',
20
+ });
21
+
22
+ child.on('close', (code: number) => {
23
+ console.log(`MarsX process ${child.pid} terminated with ${code}`);
24
+ if (code === RESTART_EXIT_CODe) {
25
+ setTimeout(spawnChildProc, 100);
26
+ } else {
27
+ process.exit(code);
28
+ }
29
+ });
30
+ };
31
+
32
+ spawnChildProc();
33
+ }
34
+ };
@@ -0,0 +1,104 @@
1
+ process.env['SUPPRESS_NO_CONFIG_WARNING'] = 'true';
2
+ import configModule from 'config';
3
+ import _ from 'lodash';
4
+ import path from 'path';
5
+ import * as yup from 'yup';
6
+ import { ValidationError } from 'yup';
7
+
8
+ export interface ImportProjectConfig {
9
+ name: string;
10
+ url: string;
11
+ api_key: string;
12
+ git_commit_ish?: string | undefined;
13
+ }
14
+
15
+ export interface Config {
16
+ production: boolean;
17
+ port: number;
18
+ blocksDir: string;
19
+ cacheDir: string;
20
+ mongoConn: string;
21
+ mongoDbName: string;
22
+ azureStorageConnection: string;
23
+ azureStorageAccountName: string;
24
+ azureStorageUrl: string;
25
+ secret: string;
26
+ webFilesTable: string;
27
+ webRecentFilesTable: string;
28
+ webFilesBlob: string;
29
+ importProjects: ImportProjectConfig[];
30
+ }
31
+
32
+ export class ConfigError extends Error {}
33
+
34
+ const ImportProjectSchema = yup.object({
35
+ name: yup.string().required(),
36
+ url: yup.string().url().required(),
37
+ api_key: yup.string().required(),
38
+ git_commit_ish: yup.string().optional(),
39
+ });
40
+
41
+ const ConfigSchema = yup.object().shape({
42
+ production: yup.boolean().required(),
43
+ port: yup.number().required().positive().integer(),
44
+ blocksDir: yup
45
+ .string()
46
+ .required()
47
+ .transform(v => path.resolve(v)),
48
+ cacheDir: yup
49
+ .string()
50
+ .required()
51
+ .transform(v => path.resolve(v)),
52
+ mongoConn: yup.string().required(),
53
+ mongoDbName: yup.string().required(),
54
+ azureStorageConnection: yup.string().required(),
55
+ azureStorageAccountName: yup.string().required(),
56
+ azureStorageUrl: yup.string().required(),
57
+ secret: yup.string().required(),
58
+ webFilesTable: yup.string().required(),
59
+ webRecentFilesTable: yup.string().required(),
60
+ webFilesBlob: yup.string().required(),
61
+ importProjects: yup.array().of(ImportProjectSchema).required(),
62
+ });
63
+
64
+ function ErrorThrowingConfig(errorMessage: string) {
65
+ return new Proxy({} as never, {
66
+ get() {
67
+ throw new ConfigError(errorMessage);
68
+ },
69
+ });
70
+ }
71
+
72
+ function validateConfig(): Config {
73
+ if (configModule.util.getConfigSources().length === 0) {
74
+ return ErrorThrowingConfig('Config file not found, ensure you have "config/default.json" file.');
75
+ }
76
+
77
+ const configObject = configModule.util.toObject();
78
+ try {
79
+ return ConfigSchema.validateSync(configObject, { abortEarly: false, stripUnknown: false });
80
+ } catch (e) {
81
+ if (e instanceof ValidationError) {
82
+ return ErrorThrowingConfig(e.errors.join('\n'));
83
+ } else {
84
+ throw e;
85
+ }
86
+ }
87
+ }
88
+
89
+ export const config = validateConfig();
90
+
91
+ function getEnvVarMapping() {
92
+ const result: Record<string, string | { __name: string; __format: string }> = {};
93
+ for (const [name, field] of Object.entries(ConfigSchema.fields)) {
94
+ const envVar = `MARSX_${_.snakeCase(name).toUpperCase()}`;
95
+ if ('type' in field && (field.type === 'object' || field.type === 'array')) {
96
+ result[name] = { __name: envVar, __format: 'json' };
97
+ } else {
98
+ result[name] = envVar;
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+
104
+ export const CustomEnvironmentVariables = getEnvVarMapping();
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { config, ImportProjectConfig, Config } from './configuration';
2
+ export { compileSfcSource, transpileTypescript } from './utils/compile';
3
+ export { parseSFC, serializeSfc, parseSfcPath, serializeSfcPath, SfcBlock, SfcPath, SfcSource } from './utils/sfc';
4
+ export { convertSfcToV3, convertV3ToSfc, V3MongoBlock } from './utils/v3';
5
+ export * as textUtils from './utils/textUtils';
6
+ export * as fileUtils from './utils/fileUtils';
@@ -0,0 +1,39 @@
1
+ import { config } from './configuration';
2
+ import { loadAllBlocks } from './loader';
3
+ import { compileSfcSource, transpileTypescript } from './utils/compile';
4
+ import { parseSFC, serializeSfc } from './utils/sfc';
5
+ import { writeFileMakeDir } from './utils/fileUtils';
6
+ import { assert } from './utils/textUtils';
7
+ import { convertSfcToV3, convertV3ToSfc } from './utils/v3';
8
+
9
+ const LAUNCHER_UTILS = {
10
+ config,
11
+ parseSFC,
12
+ serializeSfc,
13
+ convertV3ToSfc,
14
+ convertSfcToV3,
15
+ transpileTypescript,
16
+ compileSfcSource,
17
+ writeFileMakeDir,
18
+ };
19
+
20
+ export async function launchBooter(booterBlockName = 'Booter') {
21
+ const allBlocks = await loadAllBlocks();
22
+
23
+ const booterBlocks = allBlocks.filter(b => b.path.name === booterBlockName);
24
+ if (booterBlocks.length === 0)
25
+ throw new Error(`Booter block ${booterBlockName} not found. Ensure you have it locally or it is imported.`);
26
+
27
+ // Take last because order of imports matter and local blocks always override imported.
28
+ const booterBlock = booterBlocks[booterBlocks.length - 1];
29
+ assert(booterBlock);
30
+
31
+ const compiledBooterPath = await compileSfcSource(booterBlock, 'BlockFunction');
32
+ try {
33
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
34
+ await require(compiledBooterPath).default(allBlocks, LAUNCHER_UTILS);
35
+ } catch (e) {
36
+ console.error('Booter failed with:', e);
37
+ process.exit(1);
38
+ }
39
+ }
package/src/loader.ts ADDED
@@ -0,0 +1,65 @@
1
+ import axios from 'axios';
2
+ import crypto from 'crypto';
3
+ import { promises as fs } from 'fs';
4
+ import stringify from 'json-stable-stringify';
5
+ import _ from 'lodash';
6
+ import path from 'path';
7
+ import { config, ImportProjectConfig } from './configuration';
8
+ import { parseSFC, SfcBlock } from './utils/sfc';
9
+ import { isFile, listFilesRecursive, writeFileMakeDir } from './utils/fileUtils';
10
+ import { convertV3ToSfc, V3MongoBlock } from './utils/v3';
11
+
12
+ async function downloadFromExternal(externalImport: ImportProjectConfig): Promise<SfcBlock[]> {
13
+ const params = { api_key: externalImport.api_key, git_commit_ish: externalImport.git_commit_ish || '' };
14
+
15
+ console.log(`Downloading blocks from ${externalImport.url}`);
16
+ try {
17
+ const v4Resp = await axios.get<SfcBlock[]>(`${externalImport.url}/api/GetExportedAppBlocksV4`, { params });
18
+ return v4Resp.data;
19
+ } catch (e) {
20
+ console.log(`${externalImport.url} does not support V4, fallback to V3`);
21
+ }
22
+
23
+ const v3Resp = await axios.get<V3MongoBlock[]>(`${externalImport.url}/api/GetExportedAppBlocks`, { params });
24
+ return v3Resp.data.map(b => convertV3ToSfc(b));
25
+ }
26
+
27
+ async function loadCachedOrDownload(externalImport: ImportProjectConfig): Promise<SfcBlock[]> {
28
+ const hash = crypto.createHash('md5').update(stringify(externalImport)).digest('hex');
29
+ const cacheFileName = `${externalImport.name}_${externalImport.git_commit_ish}_${hash}.json`.replace(/[^\w.]+/g, '_');
30
+ const cacheFilePath = path.join(config.cacheDir, 'imports', cacheFileName);
31
+
32
+ if (await isFile(cacheFilePath)) {
33
+ const content = await fs.readFile(cacheFilePath);
34
+ console.log(`Loading cached blocks from ${externalImport.url}`);
35
+ return JSON.parse(content.toString('utf-8'));
36
+ }
37
+
38
+ const data = await downloadFromExternal(externalImport);
39
+ console.log(`Downloaded ${data.length} blocks from ${externalImport.url}`);
40
+
41
+ await writeFileMakeDir(cacheFilePath, JSON.stringify(data, null, 2), 'utf-8');
42
+ return data;
43
+ }
44
+
45
+ async function downloadAll(externalImports: ImportProjectConfig[]) {
46
+ const responses = await Promise.all(externalImports.map(loadCachedOrDownload));
47
+ return _.flatten(responses);
48
+ }
49
+
50
+ async function readBlockFile(blocksDir: string, filePath: string): Promise<SfcBlock> {
51
+ const content = await fs.readFile(filePath);
52
+ const relPath = path.relative(blocksDir, filePath);
53
+ return parseSFC(relPath, content);
54
+ }
55
+
56
+ async function readBlockFiles(blocksDir: string) {
57
+ const files = await listFilesRecursive(blocksDir);
58
+ return await Promise.all(files.filter(f => !path.basename(f).startsWith('.')).map(f => readBlockFile(blocksDir, f)));
59
+ }
60
+
61
+ export async function loadAllBlocks(): Promise<SfcBlock[]> {
62
+ const externalBlocks = await downloadAll(config.importProjects);
63
+ const localBlocks = await readBlockFiles(config.blocksDir);
64
+ return [...externalBlocks, ...localBlocks];
65
+ }
@@ -0,0 +1,59 @@
1
+ import { SourceMapPayload } from 'module';
2
+ import path from 'path';
3
+ import ts from 'typescript';
4
+ import { config } from '../configuration';
5
+ import { SfcBlock } from './sfc';
6
+ import { writeFileMakeDir } from './fileUtils';
7
+ import { assert } from './textUtils';
8
+
9
+ function genSourceMapComment(sourceMap: SourceMapPayload): string {
10
+ return `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(sourceMap)).toString('base64')}`;
11
+ }
12
+
13
+ export function transpileTypescript(
14
+ sourceCode: string,
15
+ options?: {
16
+ sourceRoot?: string;
17
+ originalFilePath?: string;
18
+ compiledFilePath?: string;
19
+ lineOffset?: number | undefined;
20
+ },
21
+ ): string {
22
+ const compiledFilePath = options?.compiledFilePath || '<UNKNOWN>.js';
23
+ const originalFilePath = options?.originalFilePath || '<UNKNOWN>.ts';
24
+
25
+ if (options?.lineOffset) {
26
+ sourceCode = '\n'.repeat(options.lineOffset) + sourceCode;
27
+ }
28
+
29
+ const transpiled = ts.transpileModule(sourceCode, {
30
+ compilerOptions: {
31
+ module: ts.ModuleKind.CommonJS,
32
+ target: ts.ScriptTarget.Latest,
33
+ esModuleInterop: true,
34
+ sourceMap: true,
35
+ // inlineSources: true,
36
+ // inlineSourceMap: true,
37
+ },
38
+ });
39
+ assert(transpiled.sourceMapText);
40
+
41
+ const sourceMap: SourceMapPayload = JSON.parse(transpiled.sourceMapText);
42
+ sourceMap.sourceRoot = options?.sourceRoot || '';
43
+ sourceMap.file = compiledFilePath;
44
+ sourceMap.sources = [originalFilePath];
45
+ const sourceMapComment = genSourceMapComment(sourceMap);
46
+ return transpiled.outputText.replace(/\n\/\/# sourceMappingURL=.+/, '') + sourceMapComment;
47
+ }
48
+
49
+ export async function compileSfcSource(sfcBlock: SfcBlock, sourceId: string): Promise<string> {
50
+ const sourceCode = sfcBlock.sources[sourceId];
51
+ if (!sourceCode) throw new Error(`Source code block ${sourceId} not found in ${sfcBlock.path.filePath}`);
52
+
53
+ const compiledFilePath = path.join(config.cacheDir, 'compiled', `${sfcBlock.path.filePath}.${sourceId}.js`);
54
+ const originalFilePath = path.join(config.blocksDir, sfcBlock.path.filePath);
55
+ const code = transpileTypescript(sourceCode.source, { compiledFilePath, originalFilePath, lineOffset: sourceCode.lineOffset });
56
+
57
+ await writeFileMakeDir(compiledFilePath, code, 'utf-8');
58
+ return compiledFilePath;
59
+ }