@scalar/import 0.0.3 → 0.0.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/CHANGELOG.md +16 -0
- package/dist/resolve.d.ts +6 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +137 -15
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @scalar/import
|
|
2
2
|
|
|
3
|
+
## 0.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 779cf35: feat: import from escaped JS object
|
|
8
|
+
- Updated dependencies [757fade]
|
|
9
|
+
- Updated dependencies [a607115]
|
|
10
|
+
- @scalar/openapi-parser@0.8.9
|
|
11
|
+
|
|
12
|
+
## 0.0.4
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 2275977: feat: import from way more sources, leverage the proxy more
|
|
17
|
+
- f2361b3: fix: resolve JSON/YAML URLs
|
|
18
|
+
|
|
3
19
|
## 0.0.3
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/resolve.d.ts
CHANGED
|
@@ -2,5 +2,10 @@
|
|
|
2
2
|
* Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
|
|
3
3
|
* This is useful to open the OpenAPI document from basically any source.
|
|
4
4
|
*/
|
|
5
|
-
export declare function resolve(value?: string | null
|
|
5
|
+
export declare function resolve(value?: string | null, options?: {
|
|
6
|
+
/**
|
|
7
|
+
* Fetch function to use instead of the global fetch. Use this to intercept requests.
|
|
8
|
+
*/
|
|
9
|
+
fetch?: (url: string) => Promise<Response>;
|
|
10
|
+
}): Promise<string | Record<string, any> | undefined>;
|
|
6
11
|
//# sourceMappingURL=resolve.d.ts.map
|
package/dist/resolve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,CAAC,EAAE;IACR;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC3C,GACA,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,CAmHnD"}
|
package/dist/resolve.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { parse } from 'yaml';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
|
|
3
5
|
* This is useful to open the OpenAPI document from basically any source.
|
|
4
6
|
*/
|
|
5
|
-
async function resolve(value) {
|
|
7
|
+
async function resolve(value, options) {
|
|
6
8
|
// URLs
|
|
7
9
|
if (value?.startsWith('http://') || value?.startsWith('https://')) {
|
|
8
10
|
// Transform GitHub URLs to raw file URLs
|
|
@@ -26,14 +28,69 @@ async function resolve(value) {
|
|
|
26
28
|
}
|
|
27
29
|
// Fetch URL
|
|
28
30
|
try {
|
|
29
|
-
|
|
31
|
+
let result = await (options?.fetch
|
|
32
|
+
? options.fetch(value)
|
|
33
|
+
: fetch(value, {
|
|
34
|
+
cache: 'no-cache',
|
|
35
|
+
}));
|
|
36
|
+
// If the custom fetch failed, try again with regular fetch
|
|
37
|
+
if (!result.ok && options?.fetch) {
|
|
38
|
+
result = await fetch(value, {
|
|
39
|
+
cache: 'no-cache',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
30
42
|
if (result.ok) {
|
|
31
43
|
const content = await result.text();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
// Check if content is directly JSON/YAML
|
|
45
|
+
try {
|
|
46
|
+
// Try parsing as JSON
|
|
47
|
+
const jsonContent = JSON.parse(content);
|
|
48
|
+
if (jsonContent.openapi || jsonContent.swagger) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Try parsing as YAML
|
|
54
|
+
try {
|
|
55
|
+
const yamlContent = parse(content);
|
|
56
|
+
if (yamlContent?.openapi || yamlContent?.swagger) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Not YAML
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const urlOrPathOrDocument = parseHtml(content);
|
|
65
|
+
// Document (string)
|
|
66
|
+
if (typeof urlOrPathOrDocument === 'string') {
|
|
67
|
+
try {
|
|
68
|
+
// JSON?
|
|
69
|
+
return JSON.parse(urlOrPathOrDocument);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// No JSON
|
|
73
|
+
try {
|
|
74
|
+
// YAML?
|
|
75
|
+
const yaml = parse(urlOrPathOrDocument);
|
|
76
|
+
if (typeof yaml === 'object') {
|
|
77
|
+
return yaml;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Not YAML
|
|
82
|
+
}
|
|
83
|
+
}
|
|
35
84
|
}
|
|
36
|
-
//
|
|
85
|
+
// Document (object)
|
|
86
|
+
if (typeof urlOrPathOrDocument === 'object') {
|
|
87
|
+
return urlOrPathOrDocument;
|
|
88
|
+
}
|
|
89
|
+
// Relative or absolute URL
|
|
90
|
+
if (urlOrPathOrDocument) {
|
|
91
|
+
return makeRelativeUrlsAbsolute(value, urlOrPathOrDocument);
|
|
92
|
+
}
|
|
93
|
+
// Check for embedded OpenAPI document
|
|
37
94
|
const embeddedSpec = parseEmbeddedOpenApi(content);
|
|
38
95
|
if (embeddedSpec) {
|
|
39
96
|
return embeddedSpec;
|
|
@@ -57,10 +114,13 @@ function parseHtml(html) {
|
|
|
57
114
|
if (!html?.includes('<')) {
|
|
58
115
|
return undefined;
|
|
59
116
|
}
|
|
60
|
-
// data-url="*"
|
|
61
|
-
const dataUrlMatch = html
|
|
62
|
-
|
|
63
|
-
|
|
117
|
+
// id="api-reference" data-url="*"
|
|
118
|
+
const dataUrlMatch = html
|
|
119
|
+
.match(/id=["']api-reference["'][\s\S]*?data-url=["']([^"']+)["']/)
|
|
120
|
+
?.slice(1)
|
|
121
|
+
.find(Boolean);
|
|
122
|
+
if (dataUrlMatch) {
|
|
123
|
+
return dataUrlMatch;
|
|
64
124
|
}
|
|
65
125
|
// spec-url="*"
|
|
66
126
|
const specUrlMatch = html.match(/spec-url=["']([^"']+)["']/);
|
|
@@ -73,9 +133,57 @@ function parseHtml(html) {
|
|
|
73
133
|
return redocInit[1];
|
|
74
134
|
}
|
|
75
135
|
// &quot;url&quot;:&quot;MY_CUSTOM_URL&quot;
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
return
|
|
136
|
+
const doubleEncodedConfigurationUrl = html.match(/&quot;url&quot;:&quot;([^;]+)&quot;/);
|
|
137
|
+
if (doubleEncodedConfigurationUrl?.[1]) {
|
|
138
|
+
return doubleEncodedConfigurationUrl[1];
|
|
139
|
+
}
|
|
140
|
+
// &quot;url&quot;:&quot;MY_CUSTOM_URL&quot;
|
|
141
|
+
const encodedConfigurationUrl = html.match(/"url":"([^;]+)"/);
|
|
142
|
+
if (encodedConfigurationUrl?.[1]) {
|
|
143
|
+
return encodedConfigurationUrl[1];
|
|
144
|
+
}
|
|
145
|
+
// Try to find embedded OpenAPI document in script tag first
|
|
146
|
+
const scriptContent = parseScriptContent(html);
|
|
147
|
+
if (scriptContent) {
|
|
148
|
+
return scriptContent;
|
|
149
|
+
}
|
|
150
|
+
// Check for configuration in script tag
|
|
151
|
+
const scriptConfigMatch = html.match(/url:\s*["']([^"']+)["']/i);
|
|
152
|
+
if (scriptConfigMatch?.[1]) {
|
|
153
|
+
return scriptConfigMatch?.[1];
|
|
154
|
+
}
|
|
155
|
+
// Check for OpenAPI URLs in the HTML
|
|
156
|
+
const linkMatch = html.match(/<a[^>]*href=["']([^"']+\.(?:yaml|yml|json))["'][^>]*>/i);
|
|
157
|
+
if (linkMatch?.[1]) {
|
|
158
|
+
return linkMatch[1];
|
|
159
|
+
}
|
|
160
|
+
// Check for URLs in escaped JS objects
|
|
161
|
+
const escapedJsonMatch = html.match(/\\"spec\\":\{.*?\\"url\\":\\"([^"\\]+)\\"/);
|
|
162
|
+
if (escapedJsonMatch?.[1]) {
|
|
163
|
+
return escapedJsonMatch[1];
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Parse OpenAPI document directly from script tag content
|
|
169
|
+
*/
|
|
170
|
+
function parseScriptContent(html) {
|
|
171
|
+
const match = html.match(/<script[^>]*id="api-reference"[^>]*>([\s\S]*?)<\/script>/);
|
|
172
|
+
if (!match?.[1])
|
|
173
|
+
return undefined;
|
|
174
|
+
try {
|
|
175
|
+
const content = match[1].trim();
|
|
176
|
+
if (content) {
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(content);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return parse(content);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error('[@scalar/import] Failed to parse script content:', error);
|
|
79
187
|
}
|
|
80
188
|
return undefined;
|
|
81
189
|
}
|
|
@@ -102,13 +210,19 @@ function makeRelativeUrlsAbsolute(baseUrl, path) {
|
|
|
102
210
|
* Parse embedded OpenAPI document from HTML
|
|
103
211
|
*/
|
|
104
212
|
function parseEmbeddedOpenApi(html) {
|
|
105
|
-
const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/);
|
|
213
|
+
const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/s);
|
|
106
214
|
if (!match)
|
|
107
215
|
return undefined;
|
|
108
216
|
try {
|
|
109
217
|
const configString = decodeHtmlEntities(match[1]);
|
|
110
218
|
const config = JSON.parse(configString);
|
|
219
|
+
// Handle both direct JSON content and YAML content
|
|
111
220
|
if (config.spec?.content) {
|
|
221
|
+
// If content is a string, assume it's YAML
|
|
222
|
+
if (typeof config.spec.content === 'string') {
|
|
223
|
+
return parse(config.spec.content);
|
|
224
|
+
}
|
|
225
|
+
// If content is an object, return it directly
|
|
112
226
|
return config.spec.content;
|
|
113
227
|
}
|
|
114
228
|
}
|
|
@@ -128,7 +242,15 @@ function decodeHtmlEntities(text) {
|
|
|
128
242
|
'"': '"',
|
|
129
243
|
''': "'",
|
|
130
244
|
};
|
|
131
|
-
|
|
245
|
+
const updatedText = text
|
|
246
|
+
.replace(new RegExp(Object.keys(entities).join('|'), 'g'), (match) => entities[match])
|
|
247
|
+
.replace(/\n/g, '\\n')
|
|
248
|
+
.trim();
|
|
249
|
+
if (updatedText.startsWith('{"')) {
|
|
250
|
+
// console.log('ok', updatedText)
|
|
251
|
+
return decodeHtmlEntities(updatedText);
|
|
252
|
+
}
|
|
253
|
+
return updatedText;
|
|
132
254
|
}
|
|
133
255
|
/**
|
|
134
256
|
* Transform GitHub URLs to raw file URLs, preserving the branch information
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"postman",
|
|
17
17
|
"scalar"
|
|
18
18
|
],
|
|
19
|
-
"version": "0.0.
|
|
19
|
+
"version": "0.0.5",
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
],
|
|
36
36
|
"module": "dist/index.js",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"
|
|
38
|
+
"yaml": "^2.4.5",
|
|
39
|
+
"@scalar/openapi-parser": "0.8.9"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"vite": "^5.4.9",
|