@scalar/import 0.0.2 → 0.0.4
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 +14 -0
- package/dist/resolve.d.ts +6 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +132 -15
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @scalar/import
|
|
2
2
|
|
|
3
|
+
## 0.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2275977: feat: import from way more sources, leverage the proxy more
|
|
8
|
+
- f2361b3: fix: resolve JSON/YAML URLs
|
|
9
|
+
|
|
10
|
+
## 0.0.3
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [7323370]
|
|
15
|
+
- @scalar/openapi-parser@0.8.8
|
|
16
|
+
|
|
3
17
|
## 0.0.2
|
|
4
18
|
|
|
5
19
|
### 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,52 @@ 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
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Parse OpenAPI document directly from script tag content
|
|
164
|
+
*/
|
|
165
|
+
function parseScriptContent(html) {
|
|
166
|
+
const match = html.match(/<script[^>]*id="api-reference"[^>]*>([\s\S]*?)<\/script>/);
|
|
167
|
+
if (!match?.[1])
|
|
168
|
+
return undefined;
|
|
169
|
+
try {
|
|
170
|
+
const content = match[1].trim();
|
|
171
|
+
if (content) {
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(content);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return parse(content);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('[@scalar/import] Failed to parse script content:', error);
|
|
79
182
|
}
|
|
80
183
|
return undefined;
|
|
81
184
|
}
|
|
@@ -102,13 +205,19 @@ function makeRelativeUrlsAbsolute(baseUrl, path) {
|
|
|
102
205
|
* Parse embedded OpenAPI document from HTML
|
|
103
206
|
*/
|
|
104
207
|
function parseEmbeddedOpenApi(html) {
|
|
105
|
-
const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/);
|
|
208
|
+
const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/s);
|
|
106
209
|
if (!match)
|
|
107
210
|
return undefined;
|
|
108
211
|
try {
|
|
109
212
|
const configString = decodeHtmlEntities(match[1]);
|
|
110
213
|
const config = JSON.parse(configString);
|
|
214
|
+
// Handle both direct JSON content and YAML content
|
|
111
215
|
if (config.spec?.content) {
|
|
216
|
+
// If content is a string, assume it's YAML
|
|
217
|
+
if (typeof config.spec.content === 'string') {
|
|
218
|
+
return parse(config.spec.content);
|
|
219
|
+
}
|
|
220
|
+
// If content is an object, return it directly
|
|
112
221
|
return config.spec.content;
|
|
113
222
|
}
|
|
114
223
|
}
|
|
@@ -128,7 +237,15 @@ function decodeHtmlEntities(text) {
|
|
|
128
237
|
'"': '"',
|
|
129
238
|
''': "'",
|
|
130
239
|
};
|
|
131
|
-
|
|
240
|
+
const updatedText = text
|
|
241
|
+
.replace(new RegExp(Object.keys(entities).join('|'), 'g'), (match) => entities[match])
|
|
242
|
+
.replace(/\n/g, '\\n')
|
|
243
|
+
.trim();
|
|
244
|
+
if (updatedText.startsWith('{"')) {
|
|
245
|
+
// console.log('ok', updatedText)
|
|
246
|
+
return decodeHtmlEntities(updatedText);
|
|
247
|
+
}
|
|
248
|
+
return updatedText;
|
|
132
249
|
}
|
|
133
250
|
/**
|
|
134
251
|
* 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.4",
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
@@ -35,10 +35,11 @@
|
|
|
35
35
|
],
|
|
36
36
|
"module": "dist/index.js",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"
|
|
38
|
+
"yaml": "^2.4.5",
|
|
39
|
+
"@scalar/openapi-parser": "0.8.8"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"vite": "^5.
|
|
42
|
+
"vite": "^5.4.9",
|
|
42
43
|
"@scalar/build-tooling": "0.1.11"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|