esm.dev 1.5.4 → 1.7.0
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/README.md +2 -2
- package/dist/cli.js +2 -0
- package/dist/commands/InitCommand.js +14 -7
- package/dist/commands/VersionCommand.js +12 -0
- package/dist/lib/queue.js +13 -5
- package/dist/lib/server.js +13 -3
- package/dist/lib/watch.js +15 -4
- package/docker-bake.hcl +24 -0
- package/package.json +7 -6
- package/src/cli.ts +2 -0
- package/src/commands/InitCommand.ts +18 -12
- package/src/commands/VersionCommand.ts +17 -0
- package/src/lib/queue.ts +20 -4
- package/src/lib/server.ts +13 -2
- package/src/lib/watch.ts +23 -7
- package/templates/init/docker-compose.yml.mustache +9 -5
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ It expects you to have a local version of ESM.sh and verdaccio running. It will
|
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Prerequisites
|
|
10
10
|
|
|
11
11
|
- You have [docker](https://www.docker.com/) installed
|
|
12
12
|
- You have [docker compose](https://docs.docker.com/compose/) installed
|
|
@@ -16,7 +16,7 @@ It expects you to have a local version of ESM.sh and verdaccio running. It will
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# Node.js
|
|
19
|
-
npx esm.dev init my-local-package-1 my-local-package-2 ...
|
|
19
|
+
npx esm.dev init ./packages/my-local-package-1 ./packages/my-local-package-2 ...
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
This will create a docker-compose file in your cwd. Now run it:
|
package/dist/cli.js
CHANGED
|
@@ -7,12 +7,14 @@ import { TokenCommand } from './commands/TokenCommand.js';
|
|
|
7
7
|
import { WaitForRegistryCommand } from './commands/WaitForRegistryCommand.js';
|
|
8
8
|
import { StartCommand } from './commands/StartCommand.js';
|
|
9
9
|
import { InitCommand } from './commands/InitCommand.js';
|
|
10
|
+
import { VersionCommand } from './commands/VersionCommand.js';
|
|
10
11
|
runExit([
|
|
11
12
|
InitCommand,
|
|
12
13
|
LoginCommand,
|
|
13
14
|
TokenCommand,
|
|
14
15
|
RepublishCommand,
|
|
15
16
|
StartCommand,
|
|
17
|
+
VersionCommand,
|
|
16
18
|
WaitForRegistryCommand,
|
|
17
19
|
WatchCommand,
|
|
18
20
|
]);
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
1
|
import { ESMDevCommand } from './ESMDevCommand.js';
|
|
3
2
|
import { PackagePathSpecific } from './mixins/PackagePathSpecific.js';
|
|
4
3
|
import { MustacheGeneratorCommand } from './MustacheGeneratorCommand.js';
|
|
5
4
|
import { Option } from 'clipanion';
|
|
5
|
+
import { isNumber } from 'typanion';
|
|
6
6
|
export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
7
7
|
static paths = [['init']];
|
|
8
8
|
static usage = ESMDevCommand.Usage({
|
|
9
9
|
description: 'Initialises your repo ready to work with ESM.sh locally',
|
|
10
|
+
examples: [
|
|
11
|
+
['Create a minimal template', 'esm.dev init'],
|
|
12
|
+
["Specify the packages you're developing", 'esm.dev init packages/*'],
|
|
13
|
+
['Specify different ports', 'esm.dev init --port 3001 --esm-origin'],
|
|
14
|
+
],
|
|
10
15
|
});
|
|
11
|
-
|
|
12
|
-
description: 'The
|
|
16
|
+
esmPort = Option.String('-e,--esm-port', '8080', {
|
|
17
|
+
description: 'The port of the ESM Server',
|
|
18
|
+
validator: isNumber(),
|
|
13
19
|
});
|
|
14
20
|
esmStoragePath = Option.String('-s,--esm-storage-path', './docker-storage/esm/esmd', {
|
|
15
21
|
description: "Path to ESM.sh's storage",
|
|
@@ -20,21 +26,22 @@ export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
|
20
26
|
outputDirectory = Option.String('-o,--output-dir', '.', {
|
|
21
27
|
description: 'The output directory',
|
|
22
28
|
});
|
|
23
|
-
|
|
24
|
-
description: 'The
|
|
29
|
+
registryPort = Option.String('-r,--registry-port', '4873', {
|
|
30
|
+
description: 'The port of your local registry',
|
|
31
|
+
validator: isNumber(),
|
|
25
32
|
});
|
|
26
33
|
esmURL;
|
|
27
34
|
npmRegistryURL;
|
|
28
35
|
packages;
|
|
29
36
|
async execute() {
|
|
37
|
+
const path = await import('node:path');
|
|
30
38
|
this.templateDir = path.resolve(import.meta.dirname, '..', '..', 'templates', 'init');
|
|
31
39
|
this.destinationDir = this.outputDirectory;
|
|
32
|
-
this.esmURL = new URL(this.esmOrigin);
|
|
33
|
-
this.npmRegistryURL = new URL(this.registry);
|
|
34
40
|
this.packages = this.packagePaths.map((packagePath) => ({
|
|
35
41
|
path: packagePath,
|
|
36
42
|
basename: path.basename(packagePath),
|
|
37
43
|
}));
|
|
44
|
+
await this.removeDestinationFile('docker-compose.yaml', 'docker-compose.yml.mustache');
|
|
38
45
|
await super.execute();
|
|
39
46
|
}
|
|
40
47
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from 'clipanion';
|
|
2
|
+
export class VersionCommand extends Command {
|
|
3
|
+
static paths = [['version']];
|
|
4
|
+
static usage = Command.Usage({
|
|
5
|
+
description: 'Display the version of esm.dev',
|
|
6
|
+
});
|
|
7
|
+
async execute() {
|
|
8
|
+
const path = await import('node:path');
|
|
9
|
+
const pckg = await Bun.file(path.resolve(import.meta.dirname, '..', '..', 'package.json')).json();
|
|
10
|
+
this.context.stdout.write(`${pckg.version}\n`);
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/lib/queue.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import debounce from 'lodash.debounce';
|
|
2
2
|
import throat from 'throat';
|
|
3
|
-
export
|
|
3
|
+
export async function queue(signal, fn) {
|
|
4
|
+
return _queue((...args) => {
|
|
5
|
+
signal.throwIfAborted();
|
|
6
|
+
return fn(...args);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
const _queue = throat.default(1);
|
|
4
10
|
/**
|
|
5
11
|
* Just like debounce, but the first call will add a promise to the queue
|
|
6
12
|
* and that promise won't resolve until the debounced function is finally called.
|
|
7
13
|
*/
|
|
8
|
-
export function queuedDebounce(fn, delay) {
|
|
14
|
+
export function queuedDebounce(fn, delay, signal) {
|
|
9
15
|
let promiseWithResolvers;
|
|
16
|
+
signal.addEventListener('abort', (reason) => promiseWithResolvers?.reject(reason));
|
|
10
17
|
const debounced = debounce(async (...args) => {
|
|
11
18
|
try {
|
|
12
19
|
const result = await fn(...args);
|
|
@@ -20,14 +27,15 @@ export function queuedDebounce(fn, delay) {
|
|
|
20
27
|
}
|
|
21
28
|
}, delay);
|
|
22
29
|
return (...args) => {
|
|
23
|
-
start();
|
|
30
|
+
const { promise } = start();
|
|
24
31
|
debounced(...args);
|
|
25
|
-
return
|
|
32
|
+
return promise;
|
|
26
33
|
};
|
|
27
34
|
function start() {
|
|
28
35
|
if (!promiseWithResolvers) {
|
|
29
36
|
promiseWithResolvers = Promise.withResolvers();
|
|
30
|
-
queue(async () => promiseWithResolvers?.promise);
|
|
37
|
+
queue(signal, async () => promiseWithResolvers?.promise);
|
|
31
38
|
}
|
|
39
|
+
return promiseWithResolvers;
|
|
32
40
|
}
|
|
33
41
|
}
|
package/dist/lib/server.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { queue } from './queue.js';
|
|
2
2
|
import { createServer } from 'node:http';
|
|
3
3
|
import httpProxy from 'http-proxy';
|
|
4
|
+
import { addAbortSignal } from 'node:stream';
|
|
4
5
|
export function serve(port, esmOrigin) {
|
|
5
6
|
const proxy = httpProxy.createProxyServer();
|
|
7
|
+
const abortController = new AbortController();
|
|
8
|
+
const signal = abortController.signal;
|
|
6
9
|
const server = createServer((req, res) => {
|
|
7
|
-
queue(() => new Promise((resolve, reject) => {
|
|
10
|
+
queue(signal, () => new Promise((resolve, reject) => {
|
|
8
11
|
console.info('Proxying', req.url);
|
|
12
|
+
addAbortSignal(signal, req);
|
|
13
|
+
addAbortSignal(signal, res);
|
|
14
|
+
req.on('error', reject);
|
|
9
15
|
res.on('error', reject);
|
|
10
16
|
res.on('close', resolve);
|
|
11
|
-
res.on('finish', resolve);
|
|
12
17
|
proxy.web(req, res, { target: esmOrigin });
|
|
13
18
|
})).catch((error) => {
|
|
14
19
|
res.statusCode = 500;
|
|
@@ -19,5 +24,10 @@ export function serve(port, esmOrigin) {
|
|
|
19
24
|
}).listen(port, () => {
|
|
20
25
|
console.info('ESM proxy server listining on', server.address());
|
|
21
26
|
});
|
|
22
|
-
return
|
|
27
|
+
return () => {
|
|
28
|
+
console.info('closing the server');
|
|
29
|
+
abortController.abort();
|
|
30
|
+
server.closeAllConnections();
|
|
31
|
+
server.close();
|
|
32
|
+
};
|
|
23
33
|
}
|
package/dist/lib/watch.js
CHANGED
|
@@ -12,18 +12,29 @@ export async function watch(packagePath, opts) {
|
|
|
12
12
|
return () => { };
|
|
13
13
|
}
|
|
14
14
|
console.info('Watching', packagePath);
|
|
15
|
+
const abortController = new AbortController();
|
|
16
|
+
const signal = abortController.signal;
|
|
15
17
|
const republishPackage = () => republish(packagePath, opts).catch(console.error);
|
|
16
18
|
const debounceRepublish = queuedDebounce((filename = '') => {
|
|
17
19
|
console.info(`Change detected at ${packagePath}/${filename}`);
|
|
18
20
|
return republishPackage();
|
|
19
|
-
}, 1_000);
|
|
20
|
-
await queue(republishPackage);
|
|
21
|
+
}, 1_000, signal);
|
|
22
|
+
await queue(signal, republishPackage);
|
|
21
23
|
if (opts.legacyMethod) {
|
|
22
|
-
|
|
24
|
+
const close = await legacyWatch(packagePath, () => debounceRepublish());
|
|
25
|
+
return () => {
|
|
26
|
+
console.info('stop watching');
|
|
27
|
+
close();
|
|
28
|
+
abortController.abort();
|
|
29
|
+
};
|
|
23
30
|
}
|
|
24
31
|
else {
|
|
25
32
|
const watcher = fsWatch(packageRoot, { recursive: true }, (_, filename) => debounceRepublish(filename ?? ''));
|
|
26
|
-
return () =>
|
|
33
|
+
return () => {
|
|
34
|
+
console.info('stop watching');
|
|
35
|
+
watcher.close();
|
|
36
|
+
abortController.abort();
|
|
37
|
+
};
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
async function legacyWatch(dirname, cb) {
|
package/docker-bake.hcl
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
group "default" {
|
|
2
|
+
targets = ["app"]
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
variable "TAG" {
|
|
6
|
+
validation {
|
|
7
|
+
condition = TAG == regex("^(?:\\d+\\.\\d+\\.\\d+)?$", TAG)
|
|
8
|
+
error_message = "The variable 'TAG' has to be in the 'x.x.x' format."
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
variable "IMAGE" {
|
|
13
|
+
default = "ghcr.io/johngeorgewright/esm.dev"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
target "app" {
|
|
17
|
+
context = "."
|
|
18
|
+
dockerfile = "Dockerfile"
|
|
19
|
+
platforms = ["linux/amd64", "linux/arm64"]
|
|
20
|
+
tags = [
|
|
21
|
+
"${IMAGE}:latest",
|
|
22
|
+
notequal("", TAG) ? "${IMAGE}:${TAG}" : ""
|
|
23
|
+
]
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esm.dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "TypeScript library template",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"esm.dev": "./dist/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
14
|
+
"build": "bun run clean && tsc",
|
|
15
|
+
"clean": "rimraf dist",
|
|
15
16
|
"esm.dev": "bun ./src/cli.ts",
|
|
16
17
|
"prepare": "husky",
|
|
17
18
|
"start": "docker compose up --detach --remove-orphans",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"lint-staged": "16.0.0",
|
|
42
43
|
"prettier": "3.5.3",
|
|
43
44
|
"rimraf": "6.0.1",
|
|
44
|
-
"semantic-release": "^24.2.
|
|
45
|
+
"semantic-release": "^24.2.4",
|
|
45
46
|
"typescript": "5.8.3"
|
|
46
47
|
},
|
|
47
48
|
"lint-staged": {
|
|
@@ -51,16 +52,16 @@
|
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"clipanion": "^4.0.0-rc.4",
|
|
54
|
-
"clipanion-generator-command": "^1.
|
|
55
|
+
"clipanion-generator-command": "^1.3.0",
|
|
55
56
|
"escape-string-regexp": "^5.0.0",
|
|
56
57
|
"glob": "^11.0.2",
|
|
57
58
|
"http-proxy": "^1.18.1",
|
|
58
59
|
"lodash.debounce": "^4.0.8",
|
|
59
60
|
"mustache": "^4.2.0",
|
|
60
|
-
"npm": "^11.
|
|
61
|
+
"npm": "^11.4.0",
|
|
61
62
|
"ramda": "^0.30.1",
|
|
62
63
|
"throat": "^6.0.2",
|
|
63
64
|
"tslib": "^2.8.1",
|
|
64
|
-
"zod": "^3.
|
|
65
|
+
"zod": "^3.25.13"
|
|
65
66
|
}
|
|
66
67
|
}
|
package/src/cli.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { TokenCommand } from './commands/TokenCommand.js'
|
|
|
7
7
|
import { WaitForRegistryCommand } from './commands/WaitForRegistryCommand.js'
|
|
8
8
|
import { StartCommand } from './commands/StartCommand.js'
|
|
9
9
|
import { InitCommand } from './commands/InitCommand.js'
|
|
10
|
+
import { VersionCommand } from './commands/VersionCommand.js'
|
|
10
11
|
|
|
11
12
|
runExit([
|
|
12
13
|
InitCommand,
|
|
@@ -14,6 +15,7 @@ runExit([
|
|
|
14
15
|
TokenCommand,
|
|
15
16
|
RepublishCommand,
|
|
16
17
|
StartCommand,
|
|
18
|
+
VersionCommand,
|
|
17
19
|
WaitForRegistryCommand,
|
|
18
20
|
WatchCommand,
|
|
19
21
|
])
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import * as path from 'node:path'
|
|
2
1
|
import { ESMDevCommand } from './ESMDevCommand.js'
|
|
3
2
|
import { PackagePathSpecific } from './mixins/PackagePathSpecific.js'
|
|
4
3
|
import { MustacheGeneratorCommand } from './MustacheGeneratorCommand.js'
|
|
5
4
|
import { Option } from 'clipanion'
|
|
5
|
+
import { isNumber } from 'typanion'
|
|
6
6
|
|
|
7
7
|
export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
8
8
|
static override paths = [['init']]
|
|
9
9
|
|
|
10
10
|
static override usage = ESMDevCommand.Usage({
|
|
11
11
|
description: 'Initialises your repo ready to work with ESM.sh locally',
|
|
12
|
+
examples: [
|
|
13
|
+
['Create a minimal template', 'esm.dev init'],
|
|
14
|
+
["Specify the packages you're developing", 'esm.dev init packages/*'],
|
|
15
|
+
['Specify different ports', 'esm.dev init --port 3001 --esm-origin'],
|
|
16
|
+
],
|
|
12
17
|
})
|
|
13
18
|
|
|
14
|
-
readonly
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
description: 'The base URL of the ESM Server',
|
|
19
|
-
},
|
|
20
|
-
)
|
|
19
|
+
readonly esmPort = Option.String('-e,--esm-port', '8080', {
|
|
20
|
+
description: 'The port of the ESM Server',
|
|
21
|
+
validator: isNumber(),
|
|
22
|
+
})
|
|
21
23
|
|
|
22
24
|
readonly esmStoragePath = Option.String(
|
|
23
25
|
'-s,--esm-storage-path',
|
|
@@ -35,8 +37,9 @@ export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
|
35
37
|
description: 'The output directory',
|
|
36
38
|
})
|
|
37
39
|
|
|
38
|
-
readonly
|
|
39
|
-
description: 'The
|
|
40
|
+
readonly registryPort = Option.String('-r,--registry-port', '4873', {
|
|
41
|
+
description: 'The port of your local registry',
|
|
42
|
+
validator: isNumber(),
|
|
40
43
|
})
|
|
41
44
|
|
|
42
45
|
esmURL!: URL
|
|
@@ -44,6 +47,7 @@ export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
|
44
47
|
packages!: { path: string; basename: string }[]
|
|
45
48
|
|
|
46
49
|
override async execute() {
|
|
50
|
+
const path = await import('node:path')
|
|
47
51
|
this.templateDir = path.resolve(
|
|
48
52
|
import.meta.dirname,
|
|
49
53
|
'..',
|
|
@@ -52,12 +56,14 @@ export class InitCommand extends PackagePathSpecific(MustacheGeneratorCommand) {
|
|
|
52
56
|
'init',
|
|
53
57
|
)
|
|
54
58
|
this.destinationDir = this.outputDirectory
|
|
55
|
-
this.esmURL = new URL(this.esmOrigin)
|
|
56
|
-
this.npmRegistryURL = new URL(this.registry)
|
|
57
59
|
this.packages = this.packagePaths.map((packagePath) => ({
|
|
58
60
|
path: packagePath,
|
|
59
61
|
basename: path.basename(packagePath),
|
|
60
62
|
}))
|
|
63
|
+
await this.removeDestinationFile(
|
|
64
|
+
'docker-compose.yaml',
|
|
65
|
+
'docker-compose.yml.mustache',
|
|
66
|
+
)
|
|
61
67
|
await super.execute()
|
|
62
68
|
}
|
|
63
69
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from 'clipanion'
|
|
2
|
+
|
|
3
|
+
export class VersionCommand extends Command {
|
|
4
|
+
static override paths = [['version']]
|
|
5
|
+
|
|
6
|
+
static override usage = Command.Usage({
|
|
7
|
+
description: 'Display the version of esm.dev',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
override async execute(): Promise<number | void> {
|
|
11
|
+
const path = await import('node:path')
|
|
12
|
+
const pckg = await Bun.file(
|
|
13
|
+
path.resolve(import.meta.dirname, '..', '..', 'package.json'),
|
|
14
|
+
).json()
|
|
15
|
+
this.context.stdout.write(`${pckg.version}\n`)
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/lib/queue.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import debounce from 'lodash.debounce'
|
|
2
2
|
import throat from 'throat'
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export async function queue<TResult, TArgs extends any[] = []>(
|
|
5
|
+
signal: AbortSignal,
|
|
6
|
+
fn: (...args: TArgs) => Promise<TResult>,
|
|
7
|
+
): Promise<TResult> {
|
|
8
|
+
return _queue((...args) => {
|
|
9
|
+
signal.throwIfAborted()
|
|
10
|
+
return fn(...(args as any))
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const _queue = throat.default(1)
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
17
|
* Just like debounce, but the first call will add a promise to the queue
|
|
@@ -10,9 +20,14 @@ export const queue = throat.default(1)
|
|
|
10
20
|
export function queuedDebounce<Args extends unknown[], R>(
|
|
11
21
|
fn: (...args: Args) => R,
|
|
12
22
|
delay: number,
|
|
23
|
+
signal: AbortSignal,
|
|
13
24
|
): (...args: Args) => Promise<R> {
|
|
14
25
|
let promiseWithResolvers: PromiseWithResolvers<R> | undefined
|
|
15
26
|
|
|
27
|
+
signal.addEventListener('abort', (reason) =>
|
|
28
|
+
promiseWithResolvers?.reject(reason),
|
|
29
|
+
)
|
|
30
|
+
|
|
16
31
|
const debounced = debounce(async (...args: Args) => {
|
|
17
32
|
try {
|
|
18
33
|
const result = await fn(...args)
|
|
@@ -25,15 +40,16 @@ export function queuedDebounce<Args extends unknown[], R>(
|
|
|
25
40
|
}, delay)
|
|
26
41
|
|
|
27
42
|
return (...args: Args) => {
|
|
28
|
-
start()
|
|
43
|
+
const { promise } = start()
|
|
29
44
|
debounced(...args)
|
|
30
|
-
return
|
|
45
|
+
return promise
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
function start() {
|
|
34
49
|
if (!promiseWithResolvers) {
|
|
35
50
|
promiseWithResolvers = Promise.withResolvers<R>()
|
|
36
|
-
queue(async () => promiseWithResolvers?.promise)
|
|
51
|
+
queue(signal, async () => promiseWithResolvers?.promise)
|
|
37
52
|
}
|
|
53
|
+
return promiseWithResolvers
|
|
38
54
|
}
|
|
39
55
|
}
|
package/src/lib/server.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { queue } from './queue.js'
|
|
2
2
|
import { createServer } from 'node:http'
|
|
3
3
|
import httpProxy from 'http-proxy'
|
|
4
|
+
import { addAbortSignal } from 'node:stream'
|
|
4
5
|
|
|
5
6
|
export function serve(port: number, esmOrigin: string) {
|
|
6
7
|
const proxy = httpProxy.createProxyServer()
|
|
8
|
+
const abortController = new AbortController()
|
|
9
|
+
const signal = abortController.signal
|
|
7
10
|
|
|
8
11
|
const server = createServer((req, res) => {
|
|
9
12
|
queue(
|
|
13
|
+
signal,
|
|
10
14
|
() =>
|
|
11
15
|
new Promise<void>((resolve, reject) => {
|
|
12
16
|
console.info('Proxying', req.url)
|
|
17
|
+
addAbortSignal(signal, req)
|
|
18
|
+
addAbortSignal(signal, res)
|
|
19
|
+
req.on('error', reject)
|
|
13
20
|
res.on('error', reject)
|
|
14
21
|
res.on('close', resolve)
|
|
15
|
-
res.on('finish', resolve)
|
|
16
22
|
proxy.web(req, res, { target: esmOrigin })
|
|
17
23
|
}),
|
|
18
24
|
).catch((error) => {
|
|
@@ -25,5 +31,10 @@ export function serve(port: number, esmOrigin: string) {
|
|
|
25
31
|
console.info('ESM proxy server listining on', server.address())
|
|
26
32
|
})
|
|
27
33
|
|
|
28
|
-
return
|
|
34
|
+
return () => {
|
|
35
|
+
console.info('closing the server')
|
|
36
|
+
abortController.abort()
|
|
37
|
+
server.closeAllConnections()
|
|
38
|
+
server.close()
|
|
39
|
+
}
|
|
29
40
|
}
|
package/src/lib/watch.ts
CHANGED
|
@@ -23,23 +23,39 @@ export async function watch(
|
|
|
23
23
|
|
|
24
24
|
console.info('Watching', packagePath)
|
|
25
25
|
|
|
26
|
+
const abortController = new AbortController()
|
|
27
|
+
const signal = abortController.signal
|
|
28
|
+
|
|
26
29
|
const republishPackage = () =>
|
|
27
30
|
republish(packagePath, opts).catch(console.error)
|
|
28
31
|
|
|
29
|
-
const debounceRepublish = queuedDebounce(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
const debounceRepublish = queuedDebounce(
|
|
33
|
+
(filename: string = '') => {
|
|
34
|
+
console.info(`Change detected at ${packagePath}/${filename}`)
|
|
35
|
+
return republishPackage()
|
|
36
|
+
},
|
|
37
|
+
1_000,
|
|
38
|
+
signal,
|
|
39
|
+
)
|
|
33
40
|
|
|
34
|
-
await queue(republishPackage)
|
|
41
|
+
await queue(signal, republishPackage)
|
|
35
42
|
|
|
36
43
|
if (opts.legacyMethod) {
|
|
37
|
-
|
|
44
|
+
const close = await legacyWatch(packagePath, () => debounceRepublish())
|
|
45
|
+
return () => {
|
|
46
|
+
console.info('stop watching')
|
|
47
|
+
close()
|
|
48
|
+
abortController.abort()
|
|
49
|
+
}
|
|
38
50
|
} else {
|
|
39
51
|
const watcher = fsWatch(packageRoot, { recursive: true }, (_, filename) =>
|
|
40
52
|
debounceRepublish(filename ?? ''),
|
|
41
53
|
)
|
|
42
|
-
return () =>
|
|
54
|
+
return () => {
|
|
55
|
+
console.info('stop watching')
|
|
56
|
+
watcher.close()
|
|
57
|
+
abortController.abort()
|
|
58
|
+
}
|
|
43
59
|
}
|
|
44
60
|
}
|
|
45
61
|
|
|
@@ -5,8 +5,8 @@ services:
|
|
|
5
5
|
- esm
|
|
6
6
|
- npm
|
|
7
7
|
environment:
|
|
8
|
-
- NPM_REGISTRY=http://npm:{{
|
|
9
|
-
- ESM_ORIGIN=http://esm:{{
|
|
8
|
+
- NPM_REGISTRY=http://npm:{{registryPort}}
|
|
9
|
+
- ESM_ORIGIN=http://esm:{{esmPort}}
|
|
10
10
|
- ESM_STORAGE_PATH=/esmd
|
|
11
11
|
- PORT={{port}}
|
|
12
12
|
command:
|
|
@@ -20,19 +20,23 @@ services:
|
|
|
20
20
|
{{#packages}}
|
|
21
21
|
- {{path}}:/watch/{{basename}}:ro
|
|
22
22
|
{{/packages}}
|
|
23
|
+
{{^packages}}
|
|
24
|
+
# EG:
|
|
25
|
+
# - ./packages/*:/watch:ro
|
|
26
|
+
{{/packages}}
|
|
23
27
|
|
|
24
28
|
esm:
|
|
25
29
|
image: ghcr.io/esm-dev/esm.sh:latest
|
|
26
30
|
environment:
|
|
27
|
-
- NPM_REGISTRY=http://npm:{{
|
|
31
|
+
- NPM_REGISTRY=http://npm:{{registryPort}}
|
|
28
32
|
- NPM_TOKEN=fake
|
|
29
33
|
- LOG_LEVEL=debug
|
|
30
34
|
ports:
|
|
31
|
-
- '{{
|
|
35
|
+
- '{{esmPort}}:8080'
|
|
32
36
|
volumes:
|
|
33
37
|
- {{esmStoragePath}}:/esmd
|
|
34
38
|
|
|
35
39
|
npm:
|
|
36
40
|
image: verdaccio/verdaccio:latest
|
|
37
41
|
ports:
|
|
38
|
-
- '{{
|
|
42
|
+
- '{{registryPort}}:4873'
|