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
142 |
x3
x3
x3
x3
x3
x3
x3
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x14
x2
x2
x14
x12
x12
x14
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x2
x3
x3
x3
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
x1
|
I
|
import * as JSONC from "@std/jsonc"
import type { Arg, Optional } 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 }
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<PropertyKey, string> & { exports?: Record<PropertyKey, 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<PropertyKey, 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()
const env = Deno.env.toObject()
try {
for (const { url, token, access } of registries) {
Deno.chdir(directory)
await command("npm", ["set", "--location", "project", "registry", url], { logger: log, env, winext: ".cmd", throw: true })
await command("npm", ["set", "--location", "project", `//${new URL(url).host}/:_authToken`, token], { logger: log, env, 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: { ...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<PropertyKey, string>
exports?: Record<PropertyKey, string>
description?: string
keywords?: string | string[]
license?: string
author?: string
homepage?: string
repository?: string
funding?: string
dependencies: Record<PropertyKey, string>
devDependencies?: Record<PropertyKey, string>
}
exports: Record<PropertyKey, string>
}
export type registry = {
url: string
token: string
access: "public" | "private"
}
|