@jsenv/package-publish 1.7.0 → 1.7.3

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.
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/package-publish",
3
- "version": "1.7.0",
3
+ "version": "1.7.3",
4
4
  "description": "Publish package to one or many registry.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -10,7 +10,8 @@
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "https://github.com/jsenv/package-publish"
13
+ "url": "https://github.com/jsenv/workflow",
14
+ "directory": "packages/jsenv-package-publish"
14
15
  },
15
16
  "engines": {
16
17
  "node": ">=16.13.0"
@@ -32,27 +33,16 @@
32
33
  "/main.js"
33
34
  ],
34
35
  "scripts": {
35
- "eslint": "node ./node_modules/eslint/bin/eslint.js . --ext=.js,.mjs",
36
- "importmap": "node ./script/importmap/importmap.mjs",
36
+ "eslint": "npx eslint . --ext=.js,.mjs",
37
37
  "test": "node --experimental-json-modules ./script/test/test.mjs",
38
38
  "test-with-coverage": "npm run test -- --coverage",
39
39
  "prettier": "prettier --write ."
40
40
  },
41
41
  "dependencies": {
42
- "@jsenv/filesystem": "2.7.1",
42
+ "@jsenv/filesystem": "3.1.0",
43
43
  "@jsenv/logger": "4.0.1",
44
- "@jsenv/server": "12.4.0",
44
+ "node-fetch": "2.6.7",
45
+ "@jsenv/log": "1.5.0",
45
46
  "semver": "7.3.5"
46
- },
47
- "devDependencies": {
48
- "@jsenv/assert": "2.4.1",
49
- "@jsenv/core": "25.3.0",
50
- "@jsenv/eslint-config": "16.0.9",
51
- "@jsenv/github-release-package": "1.3.0",
52
- "@jsenv/importmap-eslint-resolver": "5.2.5",
53
- "@jsenv/importmap-node-module": "5.1.0",
54
- "eslint": "8.7.0",
55
- "eslint-plugin-import": "2.25.4",
56
- "prettier": "2.5.1"
57
47
  }
58
48
  }
@@ -1,4 +1,4 @@
1
- # Package publish [![npm package](https://img.shields.io/npm/v/@jsenv/package-publish.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/package-publish) [![github main](https://github.com/jsenv/package-publish/workflows/main/badge.svg)](https://github.com/jsenv/package-publish/actions?workflow=main) [![codecov coverage](https://codecov.io/gh/jsenv/package-publish/branch/master/graph/badge.svg)](https://codecov.io/gh/jsenv/package-publish)
1
+ # Package publish [![npm package](https://img.shields.io/npm/v/@jsenv/package-publish.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/package-publish)
2
2
 
3
3
  Publish package to one or many registry.
4
4
 
@@ -13,8 +13,6 @@ Screenshot taken inside a github workflow when the package.json version is alrea
13
13
 
14
14
  Screenshot taken inside a github workflow when the package.json version is not published: ![publishing github workflow screenshot](./docs/publishing-github-workflow-screenshot.png)
15
15
 
16
- This package is using itself to be published on NPM. It is done during ["publish package" step](https://github.com/jsenv/package-publish/blob/0170a5c859c4732203ff2f3e70b85e705396ccc7/.github/workflows/main.yml#L70-L74) in GitHub worflow.
17
-
18
16
  # Installation
19
17
 
20
18
  ```console
@@ -31,7 +29,7 @@ _publishPackage_ is an async function publishing a package on one or many regist
31
29
  import { publishPackage } from "@jsenv/package-publish"
32
30
 
33
31
  const publishReport = await publishPackage({
34
- projectDirectoryUrl: new URL('./', import.meta.url)
32
+ rootDirectoryUrl: new URL('./', import.meta.url)
35
33
  registriesConfig: {
36
34
  "https://registry.npmjs.org": {
37
35
  token: process.env.NPM_TOKEN,
@@ -43,9 +41,9 @@ const publishReport = await publishPackage({
43
41
  })
44
42
  ```
45
43
 
46
- ## projectDirectoryUrl
44
+ ## rootDirectoryUrl
47
45
 
48
- _projectDirectoryUrl_ parameter is a string leading to a directory containing the package.json.
46
+ _rootDirectoryUrl_ parameter is a string leading to a directory containing the package.json.
49
47
 
50
48
  This parameter is **required**.
51
49
 
@@ -2,7 +2,7 @@
2
2
  // https://github.com/npm/registry-issue-archive/issues/34
3
3
  // https://stackoverflow.com/questions/53212849/querying-information-about-specific-version-of-scoped-npm-package
4
4
 
5
- import { fetchUrl } from "@jsenv/server"
5
+ import nodeFetch from "node-fetch"
6
6
 
7
7
  export const fetchLatestInRegistry = async ({
8
8
  registryUrl,
@@ -10,13 +10,17 @@ export const fetchLatestInRegistry = async ({
10
10
  token,
11
11
  }) => {
12
12
  const requestUrl = `${registryUrl}/${packageName}`
13
- const response = await fetchUrl(requestUrl, {
13
+ const response = await nodeFetch(requestUrl, {
14
14
  method: "GET",
15
15
  headers: {
16
16
  // "user-agent": "jsenv",
17
17
  accept:
18
18
  "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
19
- ...(token ? { authorization: `token ${token}` } : {}),
19
+ ...(token
20
+ ? {
21
+ authorization: `token ${token}`,
22
+ }
23
+ : {}),
20
24
  },
21
25
  })
22
26
  const responseStatus = response.status
@@ -4,152 +4,170 @@ import {
4
4
  urlToFileSystemPath,
5
5
  readFile,
6
6
  writeFile,
7
+ removeEntry,
7
8
  } from "@jsenv/filesystem"
9
+ import { UNICODE } from "@jsenv/log"
8
10
 
9
11
  import { setNpmConfig } from "./setNpmConfig.js"
10
12
 
11
13
  export const publish = async ({
12
14
  logger,
15
+ packageSlug,
13
16
  logNpmPublishOutput,
14
- projectDirectoryUrl,
17
+ rootDirectoryUrl,
15
18
  registryUrl,
16
19
  token,
17
20
  }) => {
18
- try {
19
- const promises = []
20
- const previousValue = process.env.NODE_AUTH_TOKEN
21
- const restoreProcessEnv = () => {
22
- process.env.NODE_AUTH_TOKEN = previousValue
23
- }
24
- process.env.NODE_AUTH_TOKEN = token
25
- const projectPackageFileUrl = resolveUrl(
26
- "./package.json",
27
- projectDirectoryUrl,
28
- )
29
- const projectPackageString = await readFile(projectPackageFileUrl)
30
- const restoreProjectPackageFile = () =>
31
- writeFile(projectPackageFileUrl, projectPackageString)
32
- const projectPackageObject = JSON.parse(projectPackageString)
33
- projectPackageObject.publishConfig =
34
- projectPackageObject.publishConfig || {}
35
- projectPackageObject.publishConfig.registry = registryUrl
36
- promises.push(
37
- writeFile(
38
- projectPackageFileUrl,
39
- JSON.stringify(projectPackageObject, null, " "),
40
- ),
41
- )
42
- const projectNpmConfigFileUrl = resolveUrl("./.npmrc", projectDirectoryUrl)
43
- let projectNpmConfigString
21
+ const getResult = async () => {
44
22
  try {
45
- projectNpmConfigString = await readFile(projectNpmConfigFileUrl)
46
- } catch (e) {
47
- if (e.code === "ENOENT") {
48
- projectNpmConfigString = ""
49
- } else {
50
- throw e
23
+ const promises = []
24
+ const previousValue = process.env.NODE_AUTH_TOKEN
25
+ const restoreProcessEnv = () => {
26
+ process.env.NODE_AUTH_TOKEN = previousValue
51
27
  }
52
- }
53
- const restoreProjectNpmConfigFile = () =>
54
- writeFile(projectNpmConfigFileUrl, projectNpmConfigString)
55
- promises.push(
56
- writeFile(
57
- projectNpmConfigFileUrl,
58
- setNpmConfig(projectNpmConfigString, {
59
- [computeRegistryTokenKey(registryUrl)]: token,
60
- [computeRegistryKey(projectPackageObject.name)]: registryUrl,
61
- }),
62
- ),
63
- )
64
- await Promise.all(promises)
65
- try {
66
- return await new Promise((resolve, reject) => {
67
- const command = exec(
68
- "npm publish",
69
- {
70
- cwd: urlToFileSystemPath(projectDirectoryUrl),
71
- stdio: "silent",
72
- },
73
- (error) => {
74
- if (error) {
75
- // publish conflict generally occurs because servers
76
- // returns 200 after npm publish
77
- // but returns previous version if asked immediatly
78
- // after for the last published version.
28
+ process.env.NODE_AUTH_TOKEN = token
29
+ const rootPackageFileUrl = resolveUrl("./package.json", rootDirectoryUrl)
30
+ const rootPackageString = await readFile(rootPackageFileUrl)
31
+ const restorePackageFile = () =>
32
+ writeFile(rootPackageFileUrl, rootPackageString)
33
+ const packageObject = JSON.parse(rootPackageString)
34
+ packageObject.publishConfig = packageObject.publishConfig || {}
35
+ packageObject.publishConfig.registry = registryUrl
36
+ promises.push(
37
+ writeFile(
38
+ rootPackageFileUrl,
39
+ JSON.stringify(packageObject, null, " "),
40
+ ),
41
+ )
42
+ const npmConfigFileUrl = resolveUrl("./.npmrc", rootDirectoryUrl)
43
+ let restoreNpmConfigFile
44
+ let projectNpmConfigString
45
+ try {
46
+ projectNpmConfigString = await readFile(npmConfigFileUrl)
47
+ restoreNpmConfigFile = () =>
48
+ writeFile(npmConfigFileUrl, projectNpmConfigString)
49
+ } catch (e) {
50
+ if (e.code === "ENOENT") {
51
+ restoreNpmConfigFile = () => removeEntry(npmConfigFileUrl)
52
+ projectNpmConfigString = ""
53
+ } else {
54
+ throw e
55
+ }
56
+ }
57
+ promises.push(
58
+ writeFile(
59
+ npmConfigFileUrl,
60
+ setNpmConfig(projectNpmConfigString, {
61
+ [computeRegistryTokenKey(registryUrl)]: token,
62
+ [computeRegistryKey(packageObject.name)]: registryUrl,
63
+ }),
64
+ ),
65
+ )
66
+ await Promise.all(promises)
67
+ try {
68
+ return await new Promise((resolve, reject) => {
69
+ const command = exec(
70
+ "npm publish",
71
+ {
72
+ cwd: urlToFileSystemPath(rootDirectoryUrl),
73
+ stdio: "silent",
74
+ },
75
+ (error) => {
76
+ if (error) {
77
+ // publish conflict generally occurs because servers
78
+ // returns 200 after npm publish
79
+ // but returns previous version if asked immediatly
80
+ // after for the last published version.
79
81
 
80
- // TODO: ideally we should catch 404 error returned from npm
81
- // it happens it the token is not allowed to publish
82
- // a repository. And when we detect this we display a more useful message
83
- // suggesting the token rights are insufficient to publish the package
82
+ // TODO: ideally we should catch 404 error returned from npm
83
+ // it happens it the token is not allowed to publish
84
+ // a repository. And when we detect this we display a more useful message
85
+ // suggesting the token rights are insufficient to publish the package
84
86
 
85
- // npm publish conclit
86
- if (error.message.includes("EPUBLISHCONFLICT")) {
87
- resolve({
88
- success: true,
89
- reason: "already-published",
90
- })
91
- } else if (
92
- error.message.includes("Cannot publish over existing version")
93
- ) {
94
- resolve({
95
- success: true,
96
- reason: "already-published",
97
- })
98
- } else if (
99
- error.message.includes(
100
- "You cannot publish over the previously published versions",
101
- )
102
- ) {
103
- resolve({
104
- success: true,
105
- reason: "already-published",
106
- })
107
- }
108
- // github publish conflict
109
- else if (
110
- error.message.includes(
111
- "ambiguous package version in package.json",
112
- )
113
- ) {
87
+ // npm publish conclit
88
+ if (error.message.includes("EPUBLISHCONFLICT")) {
89
+ resolve({
90
+ success: true,
91
+ reason: "already-published",
92
+ })
93
+ } else if (
94
+ error.message.includes("Cannot publish over existing version")
95
+ ) {
96
+ resolve({
97
+ success: true,
98
+ reason: "already-published",
99
+ })
100
+ } else if (
101
+ error.message.includes(
102
+ "You cannot publish over the previously published versions",
103
+ )
104
+ ) {
105
+ resolve({
106
+ success: true,
107
+ reason: "already-published",
108
+ })
109
+ }
110
+ // github publish conflict
111
+ else if (
112
+ error.message.includes(
113
+ "ambiguous package version in package.json",
114
+ )
115
+ ) {
116
+ resolve({
117
+ success: true,
118
+ reason: "already-published",
119
+ })
120
+ } else {
121
+ reject(error)
122
+ }
123
+ } else {
114
124
  resolve({
115
125
  success: true,
116
- reason: "already-published",
126
+ reason: "published",
117
127
  })
118
- } else {
119
- reject(error)
120
128
  }
121
- } else {
122
- resolve({
123
- success: true,
124
- reason: "published",
125
- })
126
- }
127
- },
128
- )
129
- if (logNpmPublishOutput) {
130
- command.stdout.on("data", (data) => {
131
- logger.debug(data)
132
- })
133
- command.stderr.on("data", (data) => {
134
- // debug because this output is part of
135
- // the error message generated by a failing npm publish
136
- logger.debug(data)
137
- })
138
- }
139
- })
140
- } finally {
141
- await Promise.all([
142
- restoreProcessEnv(),
143
- restoreProjectPackageFile(),
144
- restoreProjectNpmConfigFile(),
145
- ])
129
+ },
130
+ )
131
+ if (logNpmPublishOutput) {
132
+ command.stdout.on("data", (data) => {
133
+ logger.debug(data)
134
+ })
135
+ command.stderr.on("data", (data) => {
136
+ // debug because this output is part of
137
+ // the error message generated by a failing npm publish
138
+ logger.debug(data)
139
+ })
140
+ }
141
+ })
142
+ } finally {
143
+ await Promise.all([
144
+ restoreProcessEnv(),
145
+ restorePackageFile(),
146
+ restoreNpmConfigFile(),
147
+ ])
148
+ }
149
+ } catch (e) {
150
+ return {
151
+ success: false,
152
+ reason: e,
153
+ }
146
154
  }
147
- } catch (e) {
148
- return {
149
- success: false,
150
- reason: e,
155
+ }
156
+ const { success, reason } = await getResult()
157
+ if (success) {
158
+ if (reason === "already-published") {
159
+ logger.info(
160
+ `${UNICODE.INFO} ${packageSlug} was already published on ${registryUrl}`,
161
+ )
162
+ } else {
163
+ logger.info(`${UNICODE.OK} ${packageSlug} published on ${registryUrl}`)
151
164
  }
165
+ } else {
166
+ logger.error(`${UNICODE.FAILURE} error when publishing ${packageSlug} in ${registryUrl}
167
+ --- error stack ---
168
+ ${reason.stack}`)
152
169
  }
170
+ return { success, reason }
153
171
  }
154
172
 
155
173
  const computeRegistryTokenKey = (registryUrl) => {
@@ -1,10 +1,10 @@
1
1
  import { resolveUrl, urlToFileSystemPath, readFile } from "@jsenv/filesystem"
2
2
 
3
- export const readProjectPackage = async ({ projectDirectoryUrl }) => {
4
- const packageFileUrl = resolveUrl("./package.json", projectDirectoryUrl)
3
+ export const readProjectPackage = async ({ rootDirectoryUrl }) => {
4
+ const packageFileUrl = resolveUrl("./package.json", rootDirectoryUrl)
5
5
  let packageObject
6
6
  try {
7
- const packageString = await readFile(packageFileUrl)
7
+ const packageString = await readFile(packageFileUrl, { as: "string" })
8
8
  try {
9
9
  packageObject = JSON.parse(packageString)
10
10
  } catch (e) {
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  export const publishPackage = async ({
17
17
  logLevel,
18
- projectDirectoryUrl,
18
+ rootDirectoryUrl,
19
19
  registriesConfig,
20
20
  logNpmPublishOutput = true,
21
21
  updateProcessExitCode = true,
@@ -23,17 +23,17 @@ export const publishPackage = async ({
23
23
  const logger = createLogger({ logLevel })
24
24
  logger.debug(
25
25
  `publishPackage(${JSON.stringify(
26
- { projectDirectoryUrl, logLevel, registriesConfig },
26
+ { rootDirectoryUrl, logLevel, registriesConfig },
27
27
  null,
28
28
  " ",
29
29
  )})`,
30
30
  )
31
- projectDirectoryUrl = assertAndNormalizeDirectoryUrl(projectDirectoryUrl)
31
+ rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
32
32
  assertRegistriesConfig(registriesConfig)
33
33
 
34
34
  logger.debug(`reading project package.json`)
35
35
  const packageInProject = await readProjectPackage({
36
- projectDirectoryUrl,
36
+ rootDirectoryUrl,
37
37
  })
38
38
 
39
39
  const { name: packageName, version: packageVersion } = packageInProject
@@ -138,29 +138,15 @@ ${actionReason.stack}`)
138
138
 
139
139
  const { success, reason } = await publish({
140
140
  logger,
141
+ packageSlug: `${packageName}@${packageVersion}`,
141
142
  logNpmPublishOutput,
142
- projectDirectoryUrl,
143
+ rootDirectoryUrl,
143
144
  registryUrl,
144
145
  ...registriesConfig[registryUrl],
145
146
  })
146
147
  registryReport.actionResult = { success, reason }
147
- if (success) {
148
- if (reason === "already-published") {
149
- logger.info(
150
- `${packageName}@${packageVersion} was already published on ${registryUrl}`,
151
- )
152
- } else {
153
- logger.info(
154
- `${packageName}@${packageVersion} published on ${registryUrl}`,
155
- )
156
- }
157
- } else {
158
- logger.error(`error when publishing ${packageName}@${packageVersion} in ${registryUrl}
159
- --- error stack ---
160
- ${reason.stack}`)
161
- if (updateProcessExitCode) {
162
- process.exitCode = 1
163
- }
148
+ if (!success && updateProcessExitCode) {
149
+ process.exitCode = 1
164
150
  }
165
151
  }, Promise.resolve())
166
152