@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 +21 -0
- package/README.md +165 -0
- package/builder/build-sources.d.ts +2 -0
- package/builder/build-sources.js +57 -0
- package/builder/create-context.d.ts +3 -0
- package/builder/create-context.js +65 -0
- package/builder/extract-exports.d.ts +3 -0
- package/builder/extract-exports.js +32 -0
- package/builder/read-tsconfig.d.ts +2 -0
- package/builder/read-tsconfig.js +18 -0
- package/builder/utils/esbuild-tsc.d.ts +21 -0
- package/builder/utils/esbuild-tsc.js +90 -0
- package/builder/utils/resolve-promises.d.ts +1 -0
- package/builder/utils/resolve-promises.js +10 -0
- package/builder/utils/scan-native-deps.d.ts +2 -0
- package/builder/utils/scan-native-deps.js +74 -0
- package/builder/zip-package.d.ts +2 -0
- package/builder/zip-package.js +15 -0
- package/bundle-package.d.ts +2 -0
- package/bundle-package.js +27 -0
- package/constants.d.ts +2 -0
- package/constants.js +8 -0
- package/context.d.ts +17 -0
- package/context.js +2 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +41 -0
- package/sycbuild-cli.d.ts +1 -0
- package/sycbuild-cli.js +27 -0
- package/types.d.ts +67 -0
- package/types.js +1 -0
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,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,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,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,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,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,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,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
package/constants.js
ADDED
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
package/index.d.ts
ADDED
package/index.js
ADDED
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 {};
|
package/sycbuild-cli.js
ADDED
|
@@ -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 {};
|