@syncbridge/syncbuild 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Panates
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # @syncbridge/syncbuild
2
+
3
+ SyncBridge Extension Package Builder - A powerful tool for building and bundling SyncBridge extension packages.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @syncbridge/syncbuild
9
+ ```
10
+
11
+ ## CLI Usage
12
+
13
+ The `syncbuild` CLI tool allows you to build and bundle your SyncBridge extension packages from the command line.
14
+
15
+ ### Basic Usage
16
+
17
+ ```bash
18
+ syncbuild [options]
19
+ ```
20
+
21
+ ### CLI Options
22
+
23
+ | Option | Description | Default |
24
+ |----------------------------|-----------------------------------|-----------------|
25
+ | `-p, --path <packagePath>` | Directory of the package to build | - |
26
+ | `-o, --out <fileName>` | Output package file name | - |
27
+ | `--tsconfig <fileName>` | Path to tsconfig file | `tsconfig.json` |
28
+ | `--no-color` | Disables colors in log messages | - |
29
+ | `-V, --version` | Output the version number | - |
30
+ | `-h, --help` | Display help information | - |
31
+
32
+ ### CLI Examples
33
+
34
+ Build a package from a specific directory:
35
+
36
+ ```bash
37
+ syncbuild --path ./my-extension
38
+ ```
39
+
40
+ Build with a custom tsconfig:
41
+
42
+ ```bash
43
+ syncbuild --path ./my-extension --tsconfig tsconfig.build.json
44
+ ```
45
+
46
+ Disable colored output:
47
+
48
+ ```bash
49
+ syncbuild --path ./my-extension --no-color
50
+ ```
51
+
52
+ ## API Usage
53
+
54
+ You can also use `syncbuild` programmatically in your Node.js applications.
55
+
56
+ ### Basic API Usage
57
+
58
+ ```typescript
59
+ import { bundlePackage } from '@syncbridge/syncbuild';
60
+
61
+ await bundlePackage({
62
+ packageDir: './my-extension',
63
+ tsconfig: 'tsconfig.json'
64
+ });
65
+ ```
66
+
67
+ ### API Configuration
68
+
69
+ #### `bundlePackage(config: PackageBuildConfig): Promise<void>`
70
+
71
+ The main function for building and bundling packages.
72
+
73
+ #### `PackageBuildConfig` Interface
74
+
75
+ | Property | Type | Description | Default |
76
+ |---------------------|--------------------------|-------------------------------------------------|-----------------|
77
+ | `packageDir` | `string` | Directory of the package to be built (required) | - |
78
+ | `outDir` | `string` | Output directory for the build | - |
79
+ | `tsconfig` | `string` | Path to the tsconfig file | `tsconfig.json` |
80
+ | `external` | `Record<string, string>` | External dependencies to exclude from the build | - |
81
+ | `minify` | `boolean` | Minify the output bundle | `true` |
82
+ | `minifyWhitespace` | `boolean` | Minify whitespace in the output | `true` |
83
+ | `minifyIdentifiers` | `boolean` | Minify identifiers in the output | `true` |
84
+ | `minifySyntax` | `boolean` | Minify syntax in the output | `true` |
85
+ | `banner` | `string` | Custom banner to prepend to the output | - |
86
+ | `keepNames` | `boolean` | Keep names of variables and functions | `false` |
87
+
88
+ ### API Examples
89
+
90
+ #### Basic Build
91
+
92
+ ```typescript
93
+ import { bundlePackage } from '@syncbridge/syncbuild';
94
+
95
+ await bundlePackage({
96
+ packageDir: './my-extension'
97
+ });
98
+ ```
99
+
100
+ #### Advanced Build with Options
101
+
102
+ ```typescript
103
+ import { bundlePackage } from '@syncbridge/syncbuild';
104
+
105
+ await bundlePackage({
106
+ packageDir: './my-extension',
107
+ outDir: './dist',
108
+ tsconfig: 'tsconfig.build.json',
109
+ minify: true,
110
+ minifyWhitespace: true,
111
+ minifyIdentifiers: true,
112
+ minifySyntax: true,
113
+ keepNames: false,
114
+ external: {
115
+ 'external-module': '^1.0.0'
116
+ },
117
+ banner: '// Copyright 2024 My Company'
118
+ });
119
+ ```
120
+
121
+ #### With Error Handling
122
+
123
+ ```typescript
124
+ import { bundlePackage } from '@syncbridge/syncbuild';
125
+ import { SbError } from '@syncbridge/common';
126
+
127
+ try {
128
+ await bundlePackage({
129
+ packageDir: './my-extension',
130
+ tsconfig: 'tsconfig.json'
131
+ });
132
+ console.log('Build completed successfully!');
133
+ } catch (error) {
134
+ if (error instanceof SbError) {
135
+ console.error('Build failed:', error.message);
136
+ } else {
137
+ console.error('Unexpected error:', error);
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Build Process
143
+
144
+ The build process performs the following steps:
145
+
146
+ 1. **Context Creation** - Initializes build context with configuration
147
+ 2. **TypeScript Configuration** - Reads and validates tsconfig.json
148
+ 3. **Source Building** - Compiles TypeScript sources using esbuild
149
+ 4. **Export Extraction** - Extracts and processes package exports
150
+ 5. **Package Generation** - Creates package.json for output
151
+ 6. **Zipping** - Bundles the built package into a distributable format
152
+
153
+ ## Requirements
154
+
155
+ - Node.js >= 20.0
156
+ - TypeScript project with valid tsconfig.json
157
+ - Valid package.json in the package directory
158
+
159
+ ## License
160
+
161
+ [MIT License](LICENSE)
162
+
163
+ ## Author
164
+
165
+ Panates Inc
@@ -0,0 +1,2 @@
1
+ import { Context } from '../context.js';
2
+ export declare function buildSources(context: Context): Promise<void>;
@@ -0,0 +1,57 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { SbError } from '@syncbridge/common';
4
+ import * as esbuild from 'esbuild';
5
+ import { version } from '../constants.js';
6
+ import esbuildTsc from './utils/esbuild-tsc.js';
7
+ export async function buildSources(context) {
8
+ let entryPoint = context.entryPoint;
9
+ if (context.tsconfig?.wildcardDirectories) {
10
+ const entryPointTs = entryPoint.replace(/\.js$/, '.ts');
11
+ for (const dir of Object.keys(context.tsconfig.wildcardDirectories)) {
12
+ let filename = path.join(dir, entryPointTs);
13
+ if (fs.existsSync(filename)) {
14
+ entryPoint = filename;
15
+ break;
16
+ }
17
+ filename = path.join(dir, entryPoint);
18
+ if (fs.existsSync(filename)) {
19
+ entryPoint = filename;
20
+ break;
21
+ }
22
+ }
23
+ }
24
+ const esbuildConfig = {
25
+ absWorkingDir: context.packageDir,
26
+ entryPoints: [entryPoint],
27
+ bundle: true,
28
+ external: ['@syncbridge/common', ...Object.keys(context.externals || {})],
29
+ outfile: path.join(context.outPackageDir, context.outFile),
30
+ platform: 'node',
31
+ target: ['node22'],
32
+ format: 'esm',
33
+ mainFields: ['module', 'main'],
34
+ minify: context.config.minify ?? false,
35
+ minifyWhitespace: context.config.minifyWhitespace ?? false,
36
+ minifyIdentifiers: context.config.minifyIdentifiers ?? false,
37
+ minifySyntax: context.config.minifySyntax ?? false,
38
+ keepNames: context.config.keepNames ?? true,
39
+ banner: {
40
+ js: `/**\n Build with SyncBridge® package builder v${version}\n **/` +
41
+ "import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\n" +
42
+ (context.config.banner ?? ''),
43
+ },
44
+ plugins: [],
45
+ };
46
+ if (context.tsconfig) {
47
+ esbuildConfig.plugins?.push(esbuildTsc({
48
+ tsconfig: context.tsconfig,
49
+ }));
50
+ }
51
+ try {
52
+ await esbuild.build(esbuildConfig);
53
+ }
54
+ catch (err) {
55
+ throw new SbError(err);
56
+ }
57
+ }
@@ -0,0 +1,3 @@
1
+ import { Context } from '../context.js';
2
+ import { PackageBuildConfig } from '../types.js';
3
+ export declare function createContext(config: PackageBuildConfig): Context;
@@ -0,0 +1,65 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { deepClone } from '@jsopen/objects';
4
+ import { SbError } from '@syncbridge/common';
5
+ import * as jsonc from 'jsonc-parser';
6
+ import { version } from '../constants.js';
7
+ import { scanNativeDeps } from './utils/scan-native-deps.js';
8
+ export function createContext(config) {
9
+ let pkgJson;
10
+ try {
11
+ pkgJson = jsonc.parse(fs.readFileSync(path.resolve(config.packageDir, 'package.json'), 'utf-8'));
12
+ }
13
+ catch {
14
+ throw new SbError('package.json not found');
15
+ }
16
+ const context = {
17
+ config,
18
+ pkgJson,
19
+ outPkgJson: deepClone(pkgJson),
20
+ cwd: process.cwd(),
21
+ packageDir: path.resolve(config.packageDir),
22
+ outDir: path.resolve(process.cwd(), config.outDir ?? 'dist'),
23
+ outPackageDir: '',
24
+ outTypingsDir: '',
25
+ outFile: 'index.js',
26
+ entryPoint: pkgJson.module ?? pkgJson.main ?? 'index.js',
27
+ packageZip: '',
28
+ typingsZip: '',
29
+ };
30
+ context.outPackageDir = path.resolve(context.outDir, 'package');
31
+ context.outTypingsDir = path.resolve(context.outDir, 'typing');
32
+ context.packageZip =
33
+ pkgJson.name.replace(/^@/, '').replace(/\//g, '-') +
34
+ '-' +
35
+ pkgJson.version +
36
+ '.zip';
37
+ context.typingsZip =
38
+ pkgJson.name.replace(/^@/, '').replace(/\//g, '-') +
39
+ '-typings-' +
40
+ pkgJson.version +
41
+ '.zip';
42
+ const allDeps = {
43
+ ...pkgJson.dependencies,
44
+ ...pkgJson.optionalDependencies,
45
+ };
46
+ context.externals = {
47
+ ...scanNativeDeps(context.packageDir, Object.keys(allDeps)),
48
+ ...context.config.external,
49
+ };
50
+ /** Create package.json */
51
+ const { outPkgJson } = context;
52
+ outPkgJson.dependencies = context.externals;
53
+ delete outPkgJson.devDependencies;
54
+ delete outPkgJson.peerDependencies;
55
+ delete outPkgJson.scripts;
56
+ delete outPkgJson.main;
57
+ delete outPkgJson.exports;
58
+ outPkgJson.private = true;
59
+ outPkgJson.type = 'module';
60
+ outPkgJson.module = context.outFile;
61
+ outPkgJson.syncbridge = {
62
+ version: /^(\d+)/.exec(version)[1],
63
+ };
64
+ return context;
65
+ }
@@ -0,0 +1,3 @@
1
+ import 'reflect-metadata';
2
+ import { Context } from '../context.js';
3
+ export declare function extractExports(context: Context): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import 'reflect-metadata';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { COMPONENT_OPTIONS, PROCESSOR_OPTIONS } from '@syncbridge/common';
5
+ import { resolvePromisesDeep } from './utils/resolve-promises.js';
6
+ export async function extractExports(context) {
7
+ const { outPkgJson } = context;
8
+ const pkgGlobal = await import(path.join(context.outPackageDir, context.outFile));
9
+ for (const [k, v] of Object.entries(pkgGlobal)) {
10
+ if (typeof v !== 'function')
11
+ continue;
12
+ let metadata = Reflect.getMetadata(COMPONENT_OPTIONS, v);
13
+ if (metadata) {
14
+ metadata = await resolvePromisesDeep(metadata);
15
+ const metadataDir = path.join(context.outPackageDir, 'metadata');
16
+ fs.mkdirSync(metadataDir, { recursive: true });
17
+ fs.writeFileSync(path.join(metadataDir, `${k}.json`), JSON.stringify(metadata, undefined, 2));
18
+ outPkgJson.syncbridge.components =
19
+ outPkgJson.syncbridge.components || [];
20
+ outPkgJson.syncbridge.components.push(k);
21
+ }
22
+ metadata = Reflect.getMetadata(PROCESSOR_OPTIONS, v);
23
+ if (metadata) {
24
+ const metadataDir = path.join(context.outPackageDir, 'metadata');
25
+ fs.mkdirSync(metadataDir, { recursive: true });
26
+ fs.writeFileSync(path.join(metadataDir, `${k}.json`), JSON.stringify(metadata, undefined, 2));
27
+ outPkgJson.syncbridge.processors =
28
+ outPkgJson.syncbridge.processors || [];
29
+ outPkgJson.syncbridge.processors.push(k);
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,2 @@
1
+ import { Context } from '../context.js';
2
+ export declare function readTsconfig(context: Context): void;
@@ -0,0 +1,18 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import esbuildTsc from './utils/esbuild-tsc.js';
4
+ export function readTsconfig(context) {
5
+ /** Read tsconfig.json */
6
+ const tsconfigPath = path.resolve(context.packageDir, context.config.tsconfig || 'tsconfig.json');
7
+ if (fs.existsSync(tsconfigPath)) {
8
+ context.tsconfig = esbuildTsc.parseTsConfig(tsconfigPath, {
9
+ overwriteConfig: cfg => {
10
+ cfg.compilerOptions = cfg.compilerOptions || {};
11
+ cfg.compilerOptions.sourceMap = false;
12
+ cfg.compilerOptions.declarationDir = context.outTypingsDir;
13
+ },
14
+ });
15
+ const dtsRoot = path.join(context.packageDir, context.entryPoint.replace(/\.ts$/, '.d.ts'));
16
+ context.outPkgJson.types = path.relative(context.tsconfig.options.rootDir, dtsRoot);
17
+ }
18
+ }
@@ -0,0 +1,21 @@
1
+ import type { Plugin } from 'esbuild';
2
+ import typescript from 'typescript';
3
+ declare function esbuildTsc(options?: esbuildTsc.Options & {
4
+ cwd?: string;
5
+ overwriteConfig?: (config: any) => any;
6
+ }): Plugin;
7
+ export default esbuildTsc;
8
+ /**
9
+ * @namespace esbuildTsc
10
+ */
11
+ declare namespace esbuildTsc {
12
+ interface Options {
13
+ tsconfig?: string | typescript.ParsedCommandLine;
14
+ filter?: RegExp | ((filename: string, tsconfig: typescript.ParsedCommandLine) => boolean);
15
+ }
16
+ function parseTsConfig(tsconfig: string, options?: {
17
+ cwd?: string;
18
+ overwriteConfig?: (config: any) => any;
19
+ }): typescript.ParsedCommandLine;
20
+ function printDiagnostics(...args: any[]): void;
21
+ }
@@ -0,0 +1,90 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { inspect } from 'node:util';
4
+ import { SbError } from '@syncbridge/common';
5
+ import typescript from 'typescript';
6
+ function esbuildTsc(options) {
7
+ return {
8
+ name: 'tsc',
9
+ setup(build) {
10
+ const parsedTsConfig = options?.tsconfig && typeof options?.tsconfig === 'object'
11
+ ? options.tsconfig
12
+ : esbuildTsc.parseTsConfig((options?.tsconfig && String(options?.tsconfig)) ||
13
+ 'tsconfig.json', options);
14
+ if (parsedTsConfig.options?.sourceMap) {
15
+ parsedTsConfig.options.sourceMap = false;
16
+ parsedTsConfig.options.inlineSources = true;
17
+ parsedTsConfig.options.inlineSourceMap = true;
18
+ }
19
+ const filterFn = typeof options?.filter === 'function' ? options?.filter : undefined;
20
+ build.onLoad({
21
+ filter: typeof options?.filter === 'object' ? options.filter : /\.tsx?$/,
22
+ }, async (args) => {
23
+ if (filterFn) {
24
+ if (!filterFn(args.path, parsedTsConfig))
25
+ return;
26
+ }
27
+ else if (!parsedTsConfig?.options?.emitDecoratorMetadata) {
28
+ return;
29
+ }
30
+ const fileContent = fs.readFileSync(args.path, 'utf8');
31
+ const program = typescript.transpileModule(fileContent, {
32
+ compilerOptions: parsedTsConfig.options,
33
+ fileName: path.basename(args.path),
34
+ });
35
+ const declarationDir = parsedTsConfig.options.declarationDir ||
36
+ parsedTsConfig.options.outDir;
37
+ if (declarationDir && parsedTsConfig.options.declaration) {
38
+ const declaration = typescript.transpileDeclaration(fileContent, {
39
+ compilerOptions: parsedTsConfig.options,
40
+ fileName: path.basename(args.path),
41
+ });
42
+ const relPath = path.dirname(path.relative(parsedTsConfig.options.rootDir || options?.cwd || process.cwd(), args.path));
43
+ if (!relPath.startsWith('..')) {
44
+ const filename = path.basename(args.path, path.extname(args.path)) + '.d.ts';
45
+ const declarationPath = path.join(declarationDir, relPath, filename);
46
+ fs.mkdirSync(path.dirname(declarationPath), { recursive: true });
47
+ fs.writeFileSync(declarationPath, declaration.outputText, 'utf-8');
48
+ }
49
+ }
50
+ return { contents: program.outputText };
51
+ });
52
+ },
53
+ };
54
+ }
55
+ export default esbuildTsc;
56
+ /**
57
+ * @namespace esbuildTsc
58
+ */
59
+ (function (esbuildTsc) {
60
+ function parseTsConfig(tsconfig, options) {
61
+ const cwd = options?.cwd ?? process.cwd();
62
+ const fileName = typescript.findConfigFile(cwd, typescript.sys.fileExists, tsconfig);
63
+ // if the value was provided, but no file, fail hard
64
+ if (tsconfig !== undefined && !fileName) {
65
+ throw new SbError(`Unable to locate tsconfig'`);
66
+ }
67
+ let loadedConfig = {};
68
+ let baseDir = cwd;
69
+ if (fileName) {
70
+ const text = typescript.sys.readFile(fileName);
71
+ if (text === undefined)
72
+ throw new SbError(`failed to read '${fileName}'`);
73
+ const result = typescript.parseConfigFileTextToJson(fileName, text);
74
+ loadedConfig = result.config;
75
+ baseDir = path.dirname(fileName);
76
+ }
77
+ if (options?.overwriteConfig)
78
+ loadedConfig = options.overwriteConfig(loadedConfig) || loadedConfig;
79
+ const parsedTsConfig = typescript.parseJsonConfigFileContent(loadedConfig, typescript.sys, baseDir);
80
+ if (parsedTsConfig.errors.length) {
81
+ printDiagnostics(parsedTsConfig.errors);
82
+ }
83
+ return parsedTsConfig;
84
+ }
85
+ esbuildTsc.parseTsConfig = parseTsConfig;
86
+ function printDiagnostics(...args) {
87
+ console.log(inspect(args, false, 10, true));
88
+ }
89
+ esbuildTsc.printDiagnostics = printDiagnostics;
90
+ })(esbuildTsc || (esbuildTsc = {}));
@@ -0,0 +1 @@
1
+ export declare function resolvePromisesDeep(obj: any): Promise<any>;
@@ -0,0 +1,10 @@
1
+ import { isPlainObject } from '@jsopen/objects';
2
+ export async function resolvePromisesDeep(obj) {
3
+ obj = await obj;
4
+ if (obj && isPlainObject(obj)) {
5
+ for (const k of Object.keys(obj)) {
6
+ obj[k] = await resolvePromisesDeep(obj[k]);
7
+ }
8
+ }
9
+ return obj;
10
+ }
@@ -0,0 +1,2 @@
1
+ export declare function scanNativeDeps(rootPath: string, pkgNames: string[]): Record<string, string>;
2
+ export declare function _scanNativeDeps(rootPath: string, pkgName: string, externals: Record<string, string>, visited: Set<string>, ignoreNotFount?: boolean): void;
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4
+ import { SbError } from '@syncbridge/common';
5
+ import glob from 'fast-glob';
6
+ import * as jsonc from 'jsonc-parser';
7
+ export function scanNativeDeps(rootPath, pkgNames) {
8
+ const visited = new Set();
9
+ const externals = {};
10
+ pkgNames.forEach(pkgName => _scanNativeDeps(rootPath, pkgName, externals, visited));
11
+ return externals;
12
+ }
13
+ export function _scanNativeDeps(rootPath, pkgName, externals, visited, ignoreNotFount) {
14
+ if (visited.has(pkgName))
15
+ return;
16
+ visited.add(pkgName);
17
+ let pkgDir;
18
+ let pkgJson;
19
+ try {
20
+ try {
21
+ const location = import.meta.resolve(pkgName, pathToFileURL(rootPath));
22
+ if (location === pkgName || location.startsWith('node:'))
23
+ return;
24
+ pkgDir = path.dirname(fileURLToPath(location));
25
+ }
26
+ catch (e) {
27
+ if (e.code === 'ERR_INVALID_URL_SCHEME')
28
+ return;
29
+ if (!e.message?.includes(' find '))
30
+ throw e;
31
+ if (ignoreNotFount)
32
+ return;
33
+ throw new SbError(e);
34
+ }
35
+ let i = 3;
36
+ while (true) {
37
+ const pkgJsonPath = path.join(pkgDir, 'package.json');
38
+ if (fs.existsSync(pkgJsonPath)) {
39
+ pkgJson = jsonc.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
40
+ break;
41
+ }
42
+ if (!i--)
43
+ break;
44
+ pkgDir = path.dirname(pkgDir);
45
+ }
46
+ }
47
+ catch (e) {
48
+ if (e.code === 'ENOENT') {
49
+ console.warn('⚠ Unable to import package: ' + pkgName + '\n', e.message);
50
+ return;
51
+ }
52
+ throw e;
53
+ }
54
+ if (!pkgJson) {
55
+ console.warn(`⚠ Skipped unresolved dependency: ${pkgName}`);
56
+ return;
57
+ }
58
+ if (containsNativeBinary(pkgDir)) {
59
+ externals[pkgName] = '^' + pkgJson.version;
60
+ }
61
+ if (pkgJson.dependencies) {
62
+ for (const dep of Object.keys(pkgJson.dependencies)) {
63
+ _scanNativeDeps(rootPath, dep, externals, visited);
64
+ }
65
+ }
66
+ if (pkgJson.optionalDependencies) {
67
+ for (const dep of Object.keys(pkgJson.optionalDependencies)) {
68
+ _scanNativeDeps(rootPath, dep, externals, visited, true);
69
+ }
70
+ }
71
+ }
72
+ function containsNativeBinary(dir) {
73
+ return glob.sync('**/*.node', { cwd: dir, absolute: true }).length > 0;
74
+ }
@@ -0,0 +1,2 @@
1
+ import { Context } from '../context.js';
2
+ export declare function zipPackage(context: Context): void;
@@ -0,0 +1,15 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import AdmZip from 'adm-zip';
4
+ export function zipPackage(context) {
5
+ if (fs.existsSync(context.outPackageDir)) {
6
+ const zip = new AdmZip();
7
+ zip.addLocalFolder(context.outPackageDir);
8
+ zip.writeZip(path.join(context.outDir, context.packageZip));
9
+ }
10
+ if (fs.existsSync(context.outTypingsDir)) {
11
+ const zip = new AdmZip();
12
+ zip.addLocalFolder(context.outTypingsDir);
13
+ zip.writeZip(path.join(context.outDir, context.typingsZip));
14
+ }
15
+ }
@@ -0,0 +1,2 @@
1
+ import { PackageBuildConfig } from './types.js';
2
+ export declare function bundlePackage(config: PackageBuildConfig): Promise<void>;
@@ -0,0 +1,27 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { rimraf } from 'rimraf';
4
+ import { buildSources } from './builder/build-sources.js';
5
+ import { createContext } from './builder/create-context.js';
6
+ import { extractExports } from './builder/extract-exports.js';
7
+ import { readTsconfig } from './builder/read-tsconfig.js';
8
+ import { zipPackage } from './builder/zip-package.js';
9
+ export async function bundlePackage(config) {
10
+ const context = createContext(config);
11
+ rimraf.sync(context.outDir);
12
+ readTsconfig(context);
13
+ await buildSources(context);
14
+ await extractExports(context);
15
+ fs.writeFileSync(path.join(context.outPackageDir, 'package.json'), JSON.stringify({
16
+ ...context.outPkgJson,
17
+ types: undefined,
18
+ }, null, 2));
19
+ fs.writeFileSync(path.join(context.outTypingsDir, 'package.json'), JSON.stringify({
20
+ ...context.outPkgJson,
21
+ module: '',
22
+ syncbridge: undefined,
23
+ }, null, 2));
24
+ zipPackage(context);
25
+ // rimraf.sync(context.outPackageDir);
26
+ // rimraf.sync(context.outTypingsDir);
27
+ }
package/constants.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const version = "1";
2
+ export declare const NODE_ENV: string;
package/constants.js ADDED
@@ -0,0 +1,8 @@
1
+ import process from 'node:process';
2
+ export const version = '0.5.1';
3
+ // Environment variables
4
+ export const NODE_ENV = process.env.NODE_ENV === 'test'
5
+ ? 'test'
6
+ : (process.env.NODE_ENV || '').startsWith('dev')
7
+ ? 'dev'
8
+ : 'prod';
package/context.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { PackageBuildConfig, PackageJson } from './types.js';
2
+ export declare class Context {
3
+ pkgJson: PackageJson;
4
+ outPkgJson: PackageJson;
5
+ packageDir: string;
6
+ outDir: string;
7
+ outPackageDir: string;
8
+ outTypingsDir: string;
9
+ outFile: string;
10
+ config: PackageBuildConfig;
11
+ cwd: string;
12
+ entryPoint: string;
13
+ tsconfig?: any;
14
+ externals?: Record<string, string>;
15
+ packageZip: string;
16
+ typingsZip: string;
17
+ }
package/context.js ADDED
@@ -0,0 +1,2 @@
1
+ export class Context {
2
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './bundle-package.js';
2
+ export * from './context.js';
3
+ export * from './types.js';
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './bundle-package.js';
2
+ export * from './context.js';
3
+ export * from './types.js';
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@syncbridge/syncbuild",
3
+ "version": "0.5.1",
4
+ "description": "SyncBridge Extension Package Builder",
5
+ "author": "Panates Inc",
6
+ "license": "MIT",
7
+ "dependencies": {
8
+ "@jsopen/objects": "^2.0.2",
9
+ "adm-zip": "^0.5.16",
10
+ "ansi-colors": "^4.1.3",
11
+ "commander": "^14.0.3",
12
+ "cross-dirname": "^0.1.0",
13
+ "esbuild": "^0.27.2",
14
+ "fast-glob": "^3.3.3",
15
+ "jsonc-parser": "^3.3.1",
16
+ "putil-varhelpers": "^1.7.0",
17
+ "reflect-metadata": "^0.2.2",
18
+ "rimraf": "^6.1.2",
19
+ "ts-gems": "^3.11.3",
20
+ "typescript": "^5.9.3"
21
+ },
22
+ "peerDependencies": {
23
+ "@syncbridge/common": "^0.5.1"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./index.d.ts",
28
+ "default": "./index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "type": "module",
33
+ "module": "./index.js",
34
+ "types": "./index.d.ts",
35
+ "engines": {
36
+ "node": ">=20.0"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import * as console from 'node:console';
2
+ import * as process from 'node:process';
3
+ import { SbError } from '@syncbridge/common';
4
+ import colors from 'ansi-colors';
5
+ import { program } from 'commander';
6
+ import { bundlePackage } from './bundle-package.js';
7
+ import { version } from './constants.js';
8
+ program
9
+ .version(version)
10
+ .option('-p, --path <packagePath>', 'Directory of package')
11
+ .option('-o, --out <fileName>', 'Output package file name')
12
+ .option('--tsconfig <fileName>', 'tsconfig file name', 'tsconfig.json')
13
+ .option('--no-color', 'Disables colors in logs messages')
14
+ .action(async (options) => {
15
+ if (!options.color)
16
+ colors.enabled = false;
17
+ bundlePackage({
18
+ packageDir: options.path,
19
+ tsconfig: options.tsconfig,
20
+ }).catch(e => {
21
+ if (e instanceof SbError)
22
+ console.error(colors.red(e.message));
23
+ else
24
+ console.error(e);
25
+ });
26
+ });
27
+ program.parse(process.argv);
package/types.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ export interface PackageBuildConfig {
2
+ /**
3
+ * Represents the directory of the package to be built.
4
+ */
5
+ packageDir: string;
6
+ /**
7
+ * Represents the output directory of the build.
8
+ */
9
+ outDir?: string;
10
+ /**
11
+ * Represents the path to the tsconfig file.
12
+ */
13
+ tsconfig?: string;
14
+ /**
15
+ * List of external dependencies to exclude from the build.
16
+ */
17
+ external?: Record<string, string>;
18
+ /**
19
+ * Minify the output bundle.
20
+ * Default: true
21
+ */
22
+ minify?: boolean;
23
+ /**
24
+ * Minify whitespace in the output bundle.
25
+ * Default: true
26
+ */
27
+ minifyWhitespace?: boolean;
28
+ /**
29
+ * Minify identifiers in the output bundle.
30
+ * Default: true
31
+ */
32
+ minifyIdentifiers?: boolean;
33
+ /**
34
+ * Minify syntax in the output bundle.
35
+ * Default: true
36
+ */
37
+ minifySyntax?: boolean;
38
+ /**
39
+ * Custom banner to prepend to the output bundle.
40
+ */
41
+ banner?: string;
42
+ /**
43
+ * Keep names of variables and functions in the output bundle.
44
+ * Default: false
45
+ */
46
+ keepNames?: boolean;
47
+ }
48
+ export interface PackageJson {
49
+ name: string;
50
+ version: string;
51
+ private?: boolean;
52
+ type?: string;
53
+ module?: string;
54
+ main?: string;
55
+ types?: string;
56
+ dependencies?: Record<string, string>;
57
+ devDependencies?: Record<string, string>;
58
+ optionalDependencies?: Record<string, string>;
59
+ peerDependencies?: Record<string, string>;
60
+ scripts?: Record<string, string>;
61
+ exports?: any;
62
+ syncbridge?: {
63
+ version: string;
64
+ components?: any[];
65
+ processors?: any[];
66
+ };
67
+ }
package/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};