@nqlib/nqui 0.5.3 → 0.5.5
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/dist/components/custom/rating.d.ts.map +1 -1
- package/dist/components/ui/card.d.ts +1 -6
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/hooks/use-resolved-theme.d.ts +1 -1
- package/dist/nqui.cjs.js +23 -26
- package/dist/nqui.es.js +660 -686
- package/dist/styles.css +40 -63
- package/docs/nqui-skills/SKILL.md +3 -8
- package/package.json +1 -1
- package/scripts/build-styles.js +31 -3
- package/scripts/publish-npmjs.js +175 -98
package/dist/styles.css
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
/* Custom dark mode variant - matches when .dark class is on element or ancestor */
|
|
78
|
-
/* Non-dark
|
|
78
|
+
/* Non-dark theme — .light (warm paper) uses light: utilities */
|
|
79
79
|
/* Hit-area — https://bazza.dev/craft/2026/hit-area */
|
|
80
80
|
/* Bazza hit-area utilities — https://bazza.dev/craft/2026/hit-area */
|
|
81
81
|
|
|
@@ -792,14 +792,11 @@
|
|
|
792
792
|
nqui Additional System Variables
|
|
793
793
|
============================================ */
|
|
794
794
|
|
|
795
|
-
/*
|
|
796
|
-
|
|
795
|
+
/* Default light theme variables (.dark block follows) */
|
|
796
|
+
/* ============================================
|
|
797
797
|
nqui Color System & Design Tokens
|
|
798
798
|
============================================ */
|
|
799
799
|
|
|
800
|
-
:root + html.light = warm paper; html.mid = separate token block (showcase / comparison).
|
|
801
|
-
* Dark = .dark. Showcase app uses next-themes classes light | mid | dark.
|
|
802
|
-
*/
|
|
803
800
|
:root {
|
|
804
801
|
/* ============================================
|
|
805
802
|
Z-Index Elevation Scale
|
|
@@ -1029,18 +1026,18 @@
|
|
|
1029
1026
|
/* Light / system UI — warm paper */
|
|
1030
1027
|
--background: oklch(0.985 0.002 78);
|
|
1031
1028
|
--foreground: oklch(0.2416 0.0219 57);
|
|
1032
|
-
--card: oklch(0.
|
|
1029
|
+
--card: oklch(0.977 0.004 78);
|
|
1033
1030
|
--card-foreground: oklch(0.2416 0.0219 57);
|
|
1034
|
-
--popover: oklch(0.
|
|
1031
|
+
--popover: oklch(0.977 0.004 78);
|
|
1035
1032
|
--popover-foreground: oklch(0.2416 0.0219 57);
|
|
1036
|
-
--secondary: oklch(0.
|
|
1033
|
+
--secondary: oklch(0.928 0.008 78);
|
|
1037
1034
|
--secondary-foreground: oklch(0.3067 0.0309 56.81);
|
|
1038
|
-
--muted: oklch(0.
|
|
1035
|
+
--muted: oklch(0.914 0.007 78);
|
|
1039
1036
|
--muted-foreground: oklch(0.5576 0.0222 57.81);
|
|
1040
|
-
--accent: oklch(0.
|
|
1037
|
+
--accent: oklch(0.936 0.007 78);
|
|
1041
1038
|
--accent-foreground: oklch(0.2416 0.0219 57);
|
|
1042
|
-
--border: oklch(0.
|
|
1043
|
-
--input: oklch(0.
|
|
1039
|
+
--border: oklch(0.892 0.006 78);
|
|
1040
|
+
--input: oklch(0.892 0.006 78);
|
|
1044
1041
|
|
|
1045
1042
|
/* Chart colors */
|
|
1046
1043
|
--chart-1: oklch(0.809 0.105 251.813);
|
|
@@ -1053,48 +1050,13 @@
|
|
|
1053
1050
|
--radius: 0.45rem;
|
|
1054
1051
|
|
|
1055
1052
|
/* Sidebar */
|
|
1056
|
-
--sidebar: oklch(0.
|
|
1053
|
+
--sidebar: oklch(0.977 0.004 78);
|
|
1057
1054
|
--sidebar-foreground: oklch(0.2416 0.0219 57);
|
|
1058
1055
|
--sidebar-primary: var(--primary);
|
|
1059
1056
|
--sidebar-primary-foreground: var(--primary-foreground);
|
|
1060
|
-
--sidebar-accent: oklch(0.
|
|
1057
|
+
--sidebar-accent: oklch(0.962 0.006 78);
|
|
1061
1058
|
--sidebar-accent-foreground: oklch(0.205 0.015 55);
|
|
1062
|
-
--sidebar-border: oklch(0.
|
|
1063
|
-
--sidebar-ring: var(--ring);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
/* Mid — between warm paper and dark (neutral, slightly dimmer than light) */
|
|
1067
|
-
.mid {
|
|
1068
|
-
--background: oklch(0.915 0.01 78);
|
|
1069
|
-
--foreground: oklch(0.28 0.022 57);
|
|
1070
|
-
--card: oklch(0.895 0.012 78);
|
|
1071
|
-
--card-foreground: oklch(0.28 0.022 57);
|
|
1072
|
-
--popover: oklch(0.895 0.012 78);
|
|
1073
|
-
--popover-foreground: oklch(0.28 0.022 57);
|
|
1074
|
-
--secondary: oklch(0.88 0.012 73);
|
|
1075
|
-
--secondary-foreground: oklch(0.32 0.028 57);
|
|
1076
|
-
--muted: oklch(0.855 0.012 73);
|
|
1077
|
-
--muted-foreground: oklch(0.48 0.024 58);
|
|
1078
|
-
--accent: oklch(0.87 0.014 73);
|
|
1079
|
-
--accent-foreground: oklch(0.28 0.022 57);
|
|
1080
|
-
--border: oklch(0.82 0.016 73);
|
|
1081
|
-
--input: oklch(0.82 0.016 73);
|
|
1082
|
-
|
|
1083
|
-
--chart-1: oklch(0.809 0.105 251.813);
|
|
1084
|
-
--chart-2: oklch(0.623 0.214 259.815);
|
|
1085
|
-
--chart-3: oklch(0.546 0.245 262.881);
|
|
1086
|
-
--chart-4: oklch(0.488 0.243 264.376);
|
|
1087
|
-
--chart-5: oklch(0.424 0.199 265.638);
|
|
1088
|
-
|
|
1089
|
-
--radius: 0.45rem;
|
|
1090
|
-
|
|
1091
|
-
--sidebar: oklch(0.9 0.01 78);
|
|
1092
|
-
--sidebar-foreground: oklch(0.28 0.022 57);
|
|
1093
|
-
--sidebar-primary: var(--primary);
|
|
1094
|
-
--sidebar-primary-foreground: var(--primary-foreground);
|
|
1095
|
-
--sidebar-accent: oklch(0.875 0.012 75);
|
|
1096
|
-
--sidebar-accent-foreground: oklch(0.26 0.018 55);
|
|
1097
|
-
--sidebar-border: oklch(0.82 0.016 73);
|
|
1059
|
+
--sidebar-border: oklch(0.892 0.006 78);
|
|
1098
1060
|
--sidebar-ring: var(--ring);
|
|
1099
1061
|
}
|
|
1100
1062
|
|
|
@@ -1180,14 +1142,14 @@
|
|
|
1180
1142
|
--card-foreground: oklch(0.92 0 0);
|
|
1181
1143
|
--popover: oklch(0.205 0 0);
|
|
1182
1144
|
--popover-foreground: oklch(0.92 0 0);
|
|
1183
|
-
--secondary: oklch(0.274 0.
|
|
1145
|
+
--secondary: oklch(0.274 0.003 0);
|
|
1184
1146
|
--secondary-foreground: oklch(0.92 0 0);
|
|
1185
|
-
--muted: oklch(0.24 0 0);
|
|
1147
|
+
--muted: oklch(0.24 0.002 0);
|
|
1186
1148
|
--muted-foreground: oklch(0.708 0 0);
|
|
1187
|
-
--accent: oklch(0.27 0 0);
|
|
1149
|
+
--accent: oklch(0.27 0.003 0);
|
|
1188
1150
|
--accent-foreground: oklch(0.92 0 0);
|
|
1189
|
-
--border: oklch(
|
|
1190
|
-
--input: oklch(
|
|
1151
|
+
--border: oklch(0.34 0.004 0);
|
|
1152
|
+
--input: oklch(0.30 0.003 0);
|
|
1191
1153
|
|
|
1192
1154
|
/* Chart colors (same for both modes) */
|
|
1193
1155
|
--chart-1: oklch(0.809 0.105 251.813);
|
|
@@ -1197,17 +1159,32 @@
|
|
|
1197
1159
|
--chart-5: oklch(0.424 0.199 265.638);
|
|
1198
1160
|
|
|
1199
1161
|
/* Sidebar for dark mode */
|
|
1200
|
-
--sidebar: oklch(0.205 0 0);
|
|
1162
|
+
--sidebar: oklch(0.205 0.001 0);
|
|
1201
1163
|
--sidebar-foreground: oklch(0.92 0 0);
|
|
1202
1164
|
--sidebar-primary: var(--primary);
|
|
1203
1165
|
--sidebar-primary-foreground: var(--primary-foreground);
|
|
1204
|
-
--sidebar-accent: oklch(0.269 0 0);
|
|
1166
|
+
--sidebar-accent: oklch(0.269 0.003 0);
|
|
1205
1167
|
--sidebar-accent-foreground: oklch(0.92 0 0);
|
|
1206
|
-
--sidebar-border: oklch(
|
|
1168
|
+
--sidebar-border: oklch(0.34 0.004 0);
|
|
1207
1169
|
--sidebar-ring: oklch(0.556 0 0);
|
|
1208
1170
|
}
|
|
1209
1171
|
|
|
1210
|
-
/*
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1172
|
+
/* Elevation component styles from elevation.css */
|
|
1173
|
+
/**
|
|
1174
|
+
* Card surface elevation — semantic --border + light shadow stack.
|
|
1175
|
+
* Dark mode matches v0.4.x (no inset / multi-stack drop shadows).
|
|
1176
|
+
* Lives here with the elevation system (was JS-injected from card.tsx).
|
|
1177
|
+
*/
|
|
1178
|
+
.nqui-card {
|
|
1179
|
+
border: 1px solid color-mix(in oklch, var(--border) 50%, transparent);
|
|
1180
|
+
box-shadow:
|
|
1181
|
+
0 1px 0 0 color-mix(in oklch, var(--border) 40%, transparent),
|
|
1182
|
+
0 1px 2px 0 oklch(0.15 0 0 / 0.05);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.dark .nqui-card {
|
|
1186
|
+
border: 1px solid color-mix(in oklch, var(--border) 50%, transparent);
|
|
1187
|
+
box-shadow:
|
|
1188
|
+
0 1px 0 0 color-mix(in oklch, var(--border) 40%, transparent),
|
|
1189
|
+
0 1px 2px 0 oklch(0 0 0 / 0.3);
|
|
1190
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
## name: nqui
|
|
4
|
-
|
|
2
|
+
name: nqui
|
|
5
3
|
description: Hub for nqui component library skills and impeccable design skills. Use /nqui-components, /nqui-design-system, /impeccable, /audit, or specific skill names to invoke them.
|
|
4
|
+
---
|
|
6
5
|
|
|
7
6
|
# nqui Skills (Hub)
|
|
8
7
|
|
|
@@ -14,7 +13,6 @@ Installed into `.cursor/nqui-skills/` via `npx @nqlib/nqui init-skills`.
|
|
|
14
13
|
|
|
15
14
|
## nqui Component Library
|
|
16
15
|
|
|
17
|
-
|
|
18
16
|
| Skill | When to use |
|
|
19
17
|
| ------------------------------- | ------------------------------------------------------------------ |
|
|
20
18
|
| **nqui-components** | Implementing UI, choosing components, component props and examples |
|
|
@@ -24,14 +22,12 @@ Installed into `.cursor/nqui-skills/` via `npx @nqlib/nqui init-skills`.
|
|
|
24
22
|
| **nqui-bundle-size** | Bundle size best practices when adding deps or creating packages |
|
|
25
23
|
| **nqui-local-published-toggle** | Switching between local and published @nqlib/nqui |
|
|
26
24
|
|
|
27
|
-
|
|
28
25
|
---
|
|
29
26
|
|
|
30
27
|
## Impeccable Design Skills
|
|
31
28
|
|
|
32
29
|
Design quality and anti-pattern detection. Run `/impeccable teach` first to establish design context.
|
|
33
30
|
|
|
34
|
-
|
|
35
31
|
| Skill | When to use |
|
|
36
32
|
| -------------- | ----------------------------------------------------------------------------- |
|
|
37
33
|
| **impeccable** | Build distinctive, production-grade UI; avoid AI slop aesthetics |
|
|
@@ -52,7 +48,6 @@ Design quality and anti-pattern detection. Run `/impeccable teach` first to esta
|
|
|
52
48
|
| **/critique** | Heuristic-based UI review |
|
|
53
49
|
| **/overdrive** | Push good UI to excellent |
|
|
54
50
|
|
|
55
|
-
|
|
56
51
|
---
|
|
57
52
|
|
|
58
53
|
## Usage
|
|
@@ -62,4 +57,4 @@ Skills are invoked by name — e.g. `/nqui-components`, `/impeccable`, `/audit i
|
|
|
62
57
|
For component questions, always read the relevant doc first:
|
|
63
58
|
|
|
64
59
|
- `.cursor/nqui-skills/components/nqui-<name>.md` (after init-skills)
|
|
65
|
-
- `node_modules/@nqlib/nqui/docs/components/nqui-<name>.md` (npm install)
|
|
60
|
+
- `node_modules/@nqlib/nqui/docs/components/nqui-<name>.md` (npm install)
|
package/package.json
CHANGED
package/scripts/build-styles.js
CHANGED
|
@@ -22,6 +22,23 @@ const elevationCssPath = join(projectRoot, 'src', 'styles', 'elevation.css');
|
|
|
22
22
|
const hitAreaCssPath = join(projectRoot, 'src', 'styles', 'hit-area.css');
|
|
23
23
|
const outputPath = join(projectRoot, 'dist', 'styles.css');
|
|
24
24
|
|
|
25
|
+
function extractLayerBlock(css, layerName) {
|
|
26
|
+
const layerStart = css.indexOf(`@layer ${layerName}`);
|
|
27
|
+
if (layerStart === -1) return '';
|
|
28
|
+
const firstBrace = css.indexOf('{', layerStart);
|
|
29
|
+
if (firstBrace === -1) return '';
|
|
30
|
+
let depth = 1;
|
|
31
|
+
let i = firstBrace + 1;
|
|
32
|
+
while (i < css.length && depth > 0) {
|
|
33
|
+
const ch = css[i];
|
|
34
|
+
if (ch === '{') depth++;
|
|
35
|
+
if (ch === '}') depth--;
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
if (depth !== 0) return '';
|
|
39
|
+
return css.slice(firstBrace + 1, i - 1).trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
function extractStandaloneCSS() {
|
|
26
43
|
// Read the source CSS files
|
|
27
44
|
if (!existsSync(indexCssPath)) {
|
|
@@ -47,15 +64,22 @@ function extractStandaloneCSS() {
|
|
|
47
64
|
|
|
48
65
|
// Extract :root block from elevation.css (it's wrapped in @layer base)
|
|
49
66
|
let elevationRootContent = '';
|
|
67
|
+
let elevationComponentContent = '';
|
|
50
68
|
if (elevationCss) {
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
69
|
+
const elevationBaseLayer = extractLayerBlock(elevationCss, 'base');
|
|
70
|
+
if (elevationBaseLayer) {
|
|
71
|
+
const elevationRootMatch = elevationBaseLayer.match(/:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
72
|
+
if (elevationRootMatch) {
|
|
73
|
+
elevationRootContent = elevationRootMatch[1].trim();
|
|
74
|
+
}
|
|
54
75
|
} else {
|
|
55
76
|
// Fallback: try to extract without @layer base wrapper
|
|
56
77
|
const rootMatch = elevationCss.match(/:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
57
78
|
if (rootMatch) elevationRootContent = rootMatch[1].trim();
|
|
58
79
|
}
|
|
80
|
+
|
|
81
|
+
// Keep component-level elevation classes (e.g. .nqui-card) in dist/styles.css.
|
|
82
|
+
elevationComponentContent = extractLayerBlock(elevationCss, 'components');
|
|
59
83
|
}
|
|
60
84
|
|
|
61
85
|
// Extract :root and .dark blocks from colors.css (they're wrapped in @layer base)
|
|
@@ -229,6 +253,10 @@ function extractStandaloneCSS() {
|
|
|
229
253
|
combinedCss.slice(rootIndex);
|
|
230
254
|
}
|
|
231
255
|
|
|
256
|
+
if (elevationComponentContent) {
|
|
257
|
+
combinedCss += '\n\n/* Elevation component styles from elevation.css */\n' + elevationComponentContent + '\n';
|
|
258
|
+
}
|
|
259
|
+
|
|
232
260
|
// Clean up extra blank lines
|
|
233
261
|
combinedCss = combinedCss.replace(/\n{4,}/g, '\n\n\n');
|
|
234
262
|
|
package/scripts/publish-npmjs.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
9
|
+
import { execSync, spawn } from 'child_process';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import { dirname, join } from 'path';
|
|
12
12
|
|
|
@@ -16,116 +16,193 @@ const rootDir = join(__dirname, '..');
|
|
|
16
16
|
const packageJsonPath = join(rootDir, 'package.json');
|
|
17
17
|
const npmrcPath = join(rootDir, '.npmrc');
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
function printSslTroubleshooting() {
|
|
20
|
+
console.error(`
|
|
21
|
+
💡 TLS / network (e.g. ERR_SSL_SSL\\TLS_ALERT_BAD_RECORD_MAC) is usually environmental:
|
|
22
|
+
• Run publish again; transient failures often work on a second attempt
|
|
23
|
+
• Disable VPN or try another network; corporate proxies and inspection break TLS
|
|
24
|
+
• Update Node LTS; ensure no antivirus is MITM’ing npm traffic
|
|
25
|
+
• Retry later if registry.npmjs.com is flaking
|
|
26
|
+
• Set NPM_PUBLISH_RETRIES=5 for more attempts (default 3)
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
function isVersionAlreadyPublished(pkgName, version, cwd) {
|
|
31
|
+
try {
|
|
32
|
+
const result = execSync(`npm view ${pkgName}@${version} version --registry=https://registry.npmjs.com`, {
|
|
33
|
+
encoding: 'utf-8',
|
|
34
|
+
cwd,
|
|
35
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
36
|
+
}).trim();
|
|
37
|
+
return result === version;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const errText = `${error?.message || ''} ${error?.stderr?.toString?.() || ''}`;
|
|
40
|
+
if (errText.includes('404') || errText.includes('E404')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
npmrcExists = true;
|
|
51
|
-
originalNpmrc = readFileSync(npmrcPath, 'utf-8');
|
|
52
|
-
|
|
53
|
-
// Remove or comment out scoped registry line that points to GitHub Packages
|
|
54
|
-
const lines = originalNpmrc.split('\n');
|
|
55
|
-
const modifiedLines = lines.map(line => {
|
|
56
|
-
// Comment out scoped registry lines for @nqlib
|
|
57
|
-
if (line.trim().startsWith('@nqlib:registry=')) {
|
|
58
|
-
return `# ${line} # Temporarily disabled for npmjs.com publish`;
|
|
47
|
+
async function main() {
|
|
48
|
+
// Read package.json
|
|
49
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
50
|
+
|
|
51
|
+
// Save originals for restore
|
|
52
|
+
const originalPublishConfig = packageJson.publishConfig;
|
|
53
|
+
const originalDependencies = { ...packageJson.dependencies };
|
|
54
|
+
|
|
55
|
+
// Remove workspace:* deps (npm doesn't support workspace: protocol).
|
|
56
|
+
const workspaceDeps = ['@nqlib/nqcode', '@nqlib/nqappbuilder'];
|
|
57
|
+
for (const name of workspaceDeps) {
|
|
58
|
+
if (packageJson.dependencies?.[name] === 'workspace:*') {
|
|
59
|
+
delete packageJson.dependencies[name];
|
|
60
|
+
console.log(` Removed ${name} (workspace dep)`);
|
|
59
61
|
}
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
+
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
packageJson.publishConfig = {
|
|
65
|
+
registry: 'https://registry.npmjs.com',
|
|
66
|
+
access: 'public',
|
|
67
|
+
};
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
70
|
+
|
|
71
|
+
let originalNpmrc = null;
|
|
72
|
+
let npmrcExists = false;
|
|
73
|
+
|
|
74
|
+
if (existsSync(npmrcPath)) {
|
|
75
|
+
npmrcExists = true;
|
|
76
|
+
originalNpmrc = readFileSync(npmrcPath, 'utf-8');
|
|
77
|
+
const lines = originalNpmrc.split('\n');
|
|
78
|
+
const modifiedLines = lines.map((line) => {
|
|
79
|
+
if (line.trim().startsWith('@nqlib:registry=')) {
|
|
80
|
+
return `# ${line} # Temporarily disabled for npmjs.com publish`;
|
|
77
81
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
console.
|
|
82
|
-
console.error('💡 Please run: npm login --registry=https://registry.npmjs.com');
|
|
83
|
-
console.error('💡 Or create an access token at: https://www.npmjs.com/settings/YOUR_USERNAME/tokens');
|
|
84
|
-
throw new Error('Authentication required. Please login to npmjs.com first.');
|
|
82
|
+
return line;
|
|
83
|
+
});
|
|
84
|
+
writeFileSync(npmrcPath, modifiedLines.join('\n'));
|
|
85
|
+
console.log('📝 Temporarily modified .npmrc');
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
try {
|
|
89
|
+
console.log('🔐 Checking npm authentication...');
|
|
90
|
+
try {
|
|
91
|
+
const whoami = execSync('npm whoami --registry=https://registry.npmjs.com', {
|
|
92
|
+
encoding: 'utf-8',
|
|
93
|
+
cwd: rootDir,
|
|
94
|
+
env: {
|
|
95
|
+
...process.env,
|
|
96
|
+
npm_config_registry: 'https://registry.npmjs.com',
|
|
97
|
+
},
|
|
98
|
+
}).trim();
|
|
99
|
+
console.log(`✅ Logged in as: ${whoami}`);
|
|
100
|
+
} catch {
|
|
101
|
+
console.error('❌ Not logged in to npmjs.com');
|
|
102
|
+
console.error('💡 Please run: npm login --registry=https://registry.npmjs.com');
|
|
103
|
+
console.error(
|
|
104
|
+
'💡 Or create an access token at: https://www.npmjs.com/settings/YOUR_USERNAME/tokens'
|
|
105
|
+
);
|
|
106
|
+
throw new Error('Authentication required. Please login to npmjs.com first.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const publishEnv = {
|
|
92
110
|
...process.env,
|
|
93
|
-
|
|
94
|
-
|
|
111
|
+
npm_config_registry: 'https://registry.npmjs.com',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const pkgName = packageJson.name;
|
|
115
|
+
const pkgVersion = packageJson.version;
|
|
116
|
+
if (isVersionAlreadyPublished(pkgName, pkgVersion, rootDir)) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Version ${pkgVersion} is already published for ${pkgName}. Bump package.json version before publishing (e.g. npm version patch --no-git-tag-version).`
|
|
119
|
+
);
|
|
95
120
|
}
|
|
96
|
-
});
|
|
97
|
-
console.log('✅ Published to npmjs.com successfully!');
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error('❌ Failed to publish to npmjs.com');
|
|
100
|
-
if (error.message && error.message.includes('Authentication required')) {
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
if (error.status === 404 || (error.output && error.output.toString().includes('404'))) {
|
|
104
|
-
console.error('💡 Package not found. This might be the first publish.');
|
|
105
|
-
console.error('💡 Make sure the @nqlib scope exists on npmjs.com');
|
|
106
|
-
console.error('💡 For scoped packages, you need to own the scope or be added to it');
|
|
107
|
-
}
|
|
108
|
-
if (error.status === 401 || (error.output && error.output.toString().includes('401'))) {
|
|
109
|
-
console.error('💡 Authentication failed. Please login: npm login --registry=https://registry.npmjs.com');
|
|
110
|
-
}
|
|
111
|
-
throw error;
|
|
112
|
-
} finally {
|
|
113
|
-
// Restore original publishConfig and dependencies
|
|
114
|
-
if (originalPublishConfig) {
|
|
115
|
-
packageJson.publishConfig = originalPublishConfig;
|
|
116
|
-
} else {
|
|
117
|
-
delete packageJson.publishConfig;
|
|
118
|
-
}
|
|
119
|
-
if (originalDependencies) {
|
|
120
|
-
packageJson.dependencies = originalDependencies;
|
|
121
|
-
}
|
|
122
|
-
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
123
|
-
console.log('🔄 Restored package.json');
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
const maxAttempts = Math.min(
|
|
123
|
+
5,
|
|
124
|
+
Math.max(1, parseInt(process.env.NPM_PUBLISH_RETRIES || '3', 10) || 3)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const runPublish = () =>
|
|
128
|
+
new Promise((resolve, reject) => {
|
|
129
|
+
const child = spawn(
|
|
130
|
+
'npm',
|
|
131
|
+
['publish', '--registry=https://registry.npmjs.com', '--access', 'public'],
|
|
132
|
+
{ stdio: 'inherit', cwd: rootDir, env: publishEnv, shell: false }
|
|
133
|
+
);
|
|
134
|
+
child.on('error', reject);
|
|
135
|
+
child.on('close', (code) => {
|
|
136
|
+
if (code === 0) resolve();
|
|
137
|
+
else {
|
|
138
|
+
const err = new Error(`npm publish exited with code ${code}`);
|
|
139
|
+
err.status = code;
|
|
140
|
+
reject(err);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
console.log('📦 Publishing to npmjs.com...');
|
|
146
|
+
let lastError;
|
|
147
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
148
|
+
try {
|
|
149
|
+
await runPublish();
|
|
150
|
+
lastError = null;
|
|
151
|
+
break;
|
|
152
|
+
} catch (e) {
|
|
153
|
+
lastError = e;
|
|
154
|
+
if (attempt < maxAttempts) {
|
|
155
|
+
const wait = attempt * 2000;
|
|
156
|
+
console.warn(
|
|
157
|
+
`⚠️ Publish failed (attempt ${attempt}/${maxAttempts}). Retrying in ${wait / 1000}s (transient TLS/network often succeeds on retry)…`
|
|
158
|
+
);
|
|
159
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (lastError) throw lastError;
|
|
164
|
+
console.log('✅ Published to npmjs.com successfully!');
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('❌ Failed to publish to npmjs.com');
|
|
167
|
+
if (error.message && error.message.includes('Authentication required')) {
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
if (error.message && error.message.includes('already published')) {
|
|
171
|
+
console.error(`💡 ${error.message}`);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
const errText = `${error.message || ''} ${(error.output && error.output.toString()) || ''}`;
|
|
175
|
+
if (error.status === 404 || errText.includes('404')) {
|
|
176
|
+
console.error('💡 Package not found. This might be the first publish.');
|
|
177
|
+
console.error('💡 Make sure the @nqlib scope exists on npmjs.com');
|
|
178
|
+
console.error('💡 For scoped packages, you need to own the scope or be added to it');
|
|
179
|
+
}
|
|
180
|
+
if (error.status === 401 || errText.includes('401')) {
|
|
181
|
+
console.error(
|
|
182
|
+
'💡 Authentication failed. Please login: npm login --registry=https://registry.npmjs.com'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
printSslTroubleshooting();
|
|
186
|
+
throw error;
|
|
187
|
+
} finally {
|
|
188
|
+
if (originalPublishConfig) {
|
|
189
|
+
packageJson.publishConfig = originalPublishConfig;
|
|
190
|
+
} else {
|
|
191
|
+
delete packageJson.publishConfig;
|
|
192
|
+
}
|
|
193
|
+
if (originalDependencies) {
|
|
194
|
+
packageJson.dependencies = originalDependencies;
|
|
195
|
+
}
|
|
196
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
197
|
+
console.log('🔄 Restored package.json');
|
|
198
|
+
if (npmrcExists && originalNpmrc !== null) {
|
|
199
|
+
writeFileSync(npmrcPath, originalNpmrc);
|
|
200
|
+
console.log('🔄 Restored .npmrc');
|
|
201
|
+
}
|
|
129
202
|
}
|
|
130
203
|
}
|
|
131
204
|
|
|
205
|
+
main().catch((e) => {
|
|
206
|
+
if (e && e.message) console.error(e.message);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|