esm.dev 1.5.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/.prettierignore +4 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/commitlint.config.cjs +8 -0
- package/dist/cli.js +17 -0
- package/dist/commands/ESMDevCommand.js +6 -0
- package/dist/commands/InitCommand.js +40 -0
- package/dist/commands/LoginCommand.js +12 -0
- package/dist/commands/MustacheGeneratorCommand.js +9 -0
- package/dist/commands/RepublishCommand.js +11 -0
- package/dist/commands/StartCommand.js +16 -0
- package/dist/commands/TokenCommand.js +20 -0
- package/dist/commands/WaitForRegistryCommand.js +12 -0
- package/dist/commands/WatchCommand.js +12 -0
- package/dist/commands/mixins/CommandClass.js +1 -0
- package/dist/commands/mixins/ESMServed.js +9 -0
- package/dist/commands/mixins/ESMStorageSpecific.js +9 -0
- package/dist/commands/mixins/PackagePathSpecific.js +14 -0
- package/dist/commands/mixins/RegistrySpecific.js +9 -0
- package/dist/commands/mixins/Retryable.js +28 -0
- package/dist/commands/mixins/Servable.js +11 -0
- package/dist/commands/mixins/Watchable.js +9 -0
- package/dist/lib/MustacheGenerator.js +9 -0
- package/dist/lib/getPackageMeta.js +10 -0
- package/dist/lib/login.js +31 -0
- package/dist/lib/publish.js +5 -0
- package/dist/lib/queue.js +33 -0
- package/dist/lib/republish.js +9 -0
- package/dist/lib/server.js +23 -0
- package/dist/lib/unpublish.js +25 -0
- package/dist/lib/until.js +16 -0
- package/dist/lib/watch.js +61 -0
- package/dist/options/EnvOption.js +6 -0
- package/package.json +66 -0
- package/release.config.cjs +20 -0
- package/src/cli.ts +18 -0
- package/src/commands/ESMDevCommand.ts +8 -0
- package/src/commands/InitCommand.ts +57 -0
- package/src/commands/LoginCommand.ts +15 -0
- package/src/commands/MustacheGeneratorCommand.ts +10 -0
- package/src/commands/RepublishCommand.ts +15 -0
- package/src/commands/StartCommand.ts +21 -0
- package/src/commands/TokenCommand.ts +26 -0
- package/src/commands/WaitForRegistryCommand.ts +17 -0
- package/src/commands/WatchCommand.ts +15 -0
- package/src/commands/mixins/CommandClass.ts +3 -0
- package/src/commands/mixins/ESMServed.ts +12 -0
- package/src/commands/mixins/ESMStorageSpecific.ts +16 -0
- package/src/commands/mixins/PackagePathSpecific.ts +22 -0
- package/src/commands/mixins/RegistrySpecific.ts +12 -0
- package/src/commands/mixins/Retryable.ts +35 -0
- package/src/commands/mixins/Servable.ts +14 -0
- package/src/commands/mixins/Watchable.ts +13 -0
- package/src/commands/templates/init/docker-compose.yml.mustache +38 -0
- package/src/lib/MustacheGenerator.ts +10 -0
- package/src/lib/getPackageMeta.ts +15 -0
- package/src/lib/login.ts +35 -0
- package/src/lib/publish.ts +14 -0
- package/src/lib/queue.ts +39 -0
- package/src/lib/republish.ts +16 -0
- package/src/lib/server.ts +29 -0
- package/src/lib/unpublish.ts +43 -0
- package/src/lib/until.ts +22 -0
- package/src/lib/watch.ts +84 -0
- package/src/options/EnvOption.ts +27 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { $ } from 'bun'
|
|
2
|
+
import { glob } from 'glob'
|
|
3
|
+
import { rm } from 'node:fs/promises'
|
|
4
|
+
|
|
5
|
+
export async function unpublish({
|
|
6
|
+
registry,
|
|
7
|
+
esmStoragePath,
|
|
8
|
+
name,
|
|
9
|
+
}: {
|
|
10
|
+
registry: string
|
|
11
|
+
esmStoragePath: string
|
|
12
|
+
name: string
|
|
13
|
+
}) {
|
|
14
|
+
await Promise.all([
|
|
15
|
+
unpublishPackage(registry, name),
|
|
16
|
+
deleteESMCache(esmStoragePath, name),
|
|
17
|
+
])
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function unpublishPackage(registry: string, name: string) {
|
|
21
|
+
try {
|
|
22
|
+
await $`bunx npm unpublish --registry ${registry} --force ${name}`
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (
|
|
25
|
+
!(
|
|
26
|
+
error instanceof $.ShellError &&
|
|
27
|
+
error.stderr.includes('npm error code E404')
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
throw error
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function deleteESMCache(esmStoragePath: string, name: string) {
|
|
35
|
+
await glob(`${esmStoragePath}/**/${name}@*`).then((paths) =>
|
|
36
|
+
Promise.all(
|
|
37
|
+
paths.map((path) => {
|
|
38
|
+
console.info('Deleting', path)
|
|
39
|
+
return rm(path, { recursive: true })
|
|
40
|
+
}),
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
}
|
package/src/lib/until.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setTimeout } from 'node:timers/promises'
|
|
2
|
+
|
|
3
|
+
export async function until({
|
|
4
|
+
interval,
|
|
5
|
+
timeout,
|
|
6
|
+
try: Try,
|
|
7
|
+
}: {
|
|
8
|
+
interval: number
|
|
9
|
+
timeout: number
|
|
10
|
+
try(signal: AbortSignal): Promise<boolean>
|
|
11
|
+
}) {
|
|
12
|
+
const signal = AbortSignal.timeout(timeout)
|
|
13
|
+
while (!signal.aborted) {
|
|
14
|
+
try {
|
|
15
|
+
if (await Try(signal)) return true
|
|
16
|
+
} catch (error) {}
|
|
17
|
+
try {
|
|
18
|
+
await setTimeout(interval, { signal })
|
|
19
|
+
} catch (error) {}
|
|
20
|
+
}
|
|
21
|
+
return false
|
|
22
|
+
}
|
package/src/lib/watch.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { watch as fsWatch, watchFile, type StatsListener } from 'node:fs'
|
|
2
|
+
import { republish } from './republish.js'
|
|
3
|
+
import { getPackageMeta } from './getPackageMeta.js'
|
|
4
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
5
|
+
import { createHash } from 'node:crypto'
|
|
6
|
+
import { glob } from 'glob'
|
|
7
|
+
import { queue, queuedDebounce } from './queue.js'
|
|
8
|
+
|
|
9
|
+
export async function watch(
|
|
10
|
+
packagePath: string,
|
|
11
|
+
opts: { registry: string; esmStoragePath: string; legacyMethod?: boolean },
|
|
12
|
+
): Promise<() => void> {
|
|
13
|
+
const {
|
|
14
|
+
name,
|
|
15
|
+
packageRoot,
|
|
16
|
+
private: prvte,
|
|
17
|
+
} = await getPackageMeta(packagePath)
|
|
18
|
+
|
|
19
|
+
if (prvte) {
|
|
20
|
+
console.info(`${name} is a private package... ignoring`)
|
|
21
|
+
return () => {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.info('Watching', packagePath)
|
|
25
|
+
|
|
26
|
+
const republishPackage = () =>
|
|
27
|
+
republish(packagePath, opts).catch(console.error)
|
|
28
|
+
|
|
29
|
+
const debounceRepublish = queuedDebounce((filename: string = '') => {
|
|
30
|
+
console.info(`Change detected at ${packagePath}/${filename}`)
|
|
31
|
+
return republishPackage()
|
|
32
|
+
}, 1_000)
|
|
33
|
+
|
|
34
|
+
await queue(republishPackage)
|
|
35
|
+
|
|
36
|
+
if (opts.legacyMethod) {
|
|
37
|
+
return await legacyWatch(packagePath, () => debounceRepublish())
|
|
38
|
+
} else {
|
|
39
|
+
const watcher = fsWatch(packageRoot, { recursive: true }, (_, filename) =>
|
|
40
|
+
debounceRepublish(filename ?? ''),
|
|
41
|
+
)
|
|
42
|
+
return () => watcher.close()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function legacyWatch(dirname: string, cb: StatsListener) {
|
|
47
|
+
const filename = await hashDirectory(dirname)
|
|
48
|
+
const watcher = watchFile(filename, cb)
|
|
49
|
+
let timer: NodeJS.Timeout | undefined
|
|
50
|
+
beginTimer()
|
|
51
|
+
return () => {
|
|
52
|
+
watcher.unref()
|
|
53
|
+
clearTimeout(timer)
|
|
54
|
+
}
|
|
55
|
+
function beginTimer() {
|
|
56
|
+
timer = setTimeout(
|
|
57
|
+
() => hashDirectory(dirname).catch(console.error).finally(beginTimer),
|
|
58
|
+
1_000,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function hashDirectory(dirname: string) {
|
|
64
|
+
const filename = `/tmp/${dirname.replace(/\//g, '-')}.hash.txt`
|
|
65
|
+
const files = await glob(`${dirname}/**/*`, { nodir: true })
|
|
66
|
+
const hashes = await Promise.all(
|
|
67
|
+
files.map(async (file) => {
|
|
68
|
+
const contents = await readFile(file, 'utf-8')
|
|
69
|
+
return createHash('sha256').update(contents).digest('hex')
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
const newHash = hashes.join('')
|
|
73
|
+
|
|
74
|
+
let previousHash = ''
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
previousHash = await readFile(filename, 'utf-8')
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
if (error.code !== 'ENOENT') throw error
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (newHash !== previousHash) await writeFile(filename, newHash)
|
|
83
|
+
return filename
|
|
84
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Option } from 'clipanion'
|
|
2
|
+
import type { StrictValidator } from 'typanion'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @see https://github.com/arcanis/clipanion/issues/174
|
|
6
|
+
*/
|
|
7
|
+
export function EnvOption<T extends {}>(
|
|
8
|
+
envName: string,
|
|
9
|
+
descriptor: string,
|
|
10
|
+
opts: { description?: string; validator: StrictValidator<unknown, T> },
|
|
11
|
+
): T
|
|
12
|
+
|
|
13
|
+
export function EnvOption(
|
|
14
|
+
envName: string,
|
|
15
|
+
descriptor: string,
|
|
16
|
+
opts?: { description?: string },
|
|
17
|
+
): string
|
|
18
|
+
|
|
19
|
+
export function EnvOption<T extends {}>(
|
|
20
|
+
envName: string,
|
|
21
|
+
descriptor: string,
|
|
22
|
+
opts: { description?: string; validator?: StrictValidator<unknown, T> } = {},
|
|
23
|
+
) {
|
|
24
|
+
return process.env[envName]
|
|
25
|
+
? Option.String(descriptor, process.env[envName], opts)
|
|
26
|
+
: Option.String(descriptor, { ...opts, required: true })
|
|
27
|
+
}
|