1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 |
x1
x3
x3
x3
x3
x3
x3
x3
x3
x3
x3
x5
x5
x5
x5
x5
x5
x5
x5
x20
x5
x5
x5
x5
x5
x5
x5
x5
x5
x5
x5
x45
x19
x21
x21
x19
x31
x31
x19
x5
x5
x5
x5
x5
x5
x5
x5
x15
x5
x5
x5
x5
x35
x5
x3
x3
x3
x3
x4
x16
x4
x4
x4
x4
x4
x4
x4
x4
x4
x4
x44
x44
x32
x4
x4
x4
x4
x4
x4
x4
x28
x4
x4
x4
x4
x20
x4
|
I
|
import * as JSONC from "@std/jsonc"
import type { Arg, Optional, record } from "@libs/typing"
import { assertMatch } from "@std/assert"
import { Logger } from "@libs/logger"
import { bundle } from "../bundle.ts"
import { dirname, resolve, toFileUrl } from "@std/path"
import { command } from "@libs/run/command"
export type { Arg, Logger, record }
export async function packaged(path = "deno.jsonc", { logger: log = new Logger(), scope = undefined as Optional<string>, name = undefined as Optional<string> } = {}): Promise<package_output> {
path = resolve(path)
log.debug(`processing: ${path}`)
const mod = JSONC.parse(await Deno.readTextFile(path)) as record<string> & { exports?: record<string> }
assertMatch(mod.name, /^@[a-z0-9-]+\/[a-z0-9-]+$/)
const [_scope, _name] = mod.name.split("/")
scope ??= _scope
name ??= _name
log = log.with({ scope, name }).debug()
const json = {
name: `${scope}/${name}`,
version: mod.version,
type: "module",
scripts: {},
dependencies: {},
devDependencies: {},
} as package_output["json"]
log.trace(`set version: ${json.version}`)
for (const key of ["description", "keywords", "license", "author", "homepage", "repository", "funding"] as const) {
if (mod[key]) {
json[key] = mod[key]
log.trace(`set ${key}: ${json[key]}`)
} else {
log.trace(`skipped ${key}`)
}
}
const exports = {} as record<string>
if (mod.exports) {
json.exports = {}
for (const [key, value] of Object.entries(mod.exports) as [string, string][]) {
const url = toFileUrl(resolve(dirname(path), value))
const file = value.replace(/\.ts$/, ".mjs")
log.trace(`bundling: ${file}`)
const code = await bundle(url, { config: toFileUrl(path) })
json.exports[key] = file
exports[file] = code
}
}
return { directory: dirname(path), scope, name, json, exports } as package_output
}
export async function publish(
path: Arg<typeof packaged>,
{ logger: log = new Logger(), registries = [], dryrun, provenance, ...options }: Arg<typeof packaged, 1> & { logger?: Logger; registries?: registry[]; dryrun?: boolean; provenance?: boolean },
): Promise<Pick<package_output, "scope" | "name" | "json">> {
const { directory, scope, name, json, exports } = await packaged(path, options)
log = log.with({ scope, name })
log.debug("creating: package.json")
await Deno.writeTextFile(resolve(directory, "package.json"), JSON.stringify(json, null, 2))
for (const [key, value] of Object.entries(exports)) {
log.debug(`creating export: ${key}`)
await Deno.writeTextFile(resolve(directory, key), value)
}
const cwd = Deno.cwd()
try {
for (const { url, token, access } of registries) {
Deno.chdir(directory)
await command("npm", ["set", "--location", "project", "registry", url], { logger: log, winext: ".cmd", throw: true })
await command("npm", ["set", "--location", "project", `//${new URL(url).host}/:_authToken`, token], { logger: log, winext: ".cmd", throw: true })
const args = ["publish", "--access", { public: "public", private: "restricted" }[access]]
if (dryrun) {
args.push("--dry-run")
}
if (provenance) {
args.push("--provenance")
}
log.debug(`publishing to: ${url} (${access})`)
const { success, stdout, stderr } = await command("npm", args, { logger: log, env: { NPM_TOKEN: token }, winext: ".cmd" })
if ((!success) && (!`${stdout}\n${stderr}`.includes("You cannot publish over the previously published versions"))) {
throw new Error(`npm publish failed: ${stdout}\n${stderr}`)
}
}
} finally {
Deno.chdir(cwd)
}
return { scope, name, json }
}
export type package_output = {
directory: string
scope: string
name: string
json: {
name: string
version: string
type: "module"
scripts: record<string>
exports?: record<string>
description?: string
keywords?: string | string[]
license?: string
author?: string
homepage?: string
repository?: string
funding?: string
dependencies: record<string>
devDependencies?: record<string>
}
exports: record<string>
}
export type registry = {
url: string
token: string
access: "public" | "private"
}
|