source: imaps-frontend/node_modules/webpack/lib/sharing/utils.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 10.0 KB
RevLine 
[79a0317]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { join, dirname, readJson } = require("../util/fs");
9
10/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
11/** @typedef {import("../util/fs").JsonObject} JsonObject */
12/** @typedef {import("../util/fs").JsonPrimitive} JsonPrimitive */
13
14// Extreme shorthand only for github. eg: foo/bar
15const RE_URL_GITHUB_EXTREME_SHORT = /^[^/@:.\s][^/@:\s]*\/[^@:\s]*[^/@:\s]#\S+/;
16
17// Short url with specific protocol. eg: github:foo/bar
18const RE_GIT_URL_SHORT = /^(github|gitlab|bitbucket|gist):\/?[^/.]+\/?/i;
19
20// Currently supported protocols
21const RE_PROTOCOL =
22 /^((git\+)?(ssh|https?|file)|git|github|gitlab|bitbucket|gist):$/i;
23
24// Has custom protocol
25const RE_CUSTOM_PROTOCOL = /^((git\+)?(ssh|https?|file)|git):\/\//i;
26
27// Valid hash format for npm / yarn ...
28const RE_URL_HASH_VERSION = /#(?:semver:)?(.+)/;
29
30// Simple hostname validate
31const RE_HOSTNAME = /^(?:[^/.]+(\.[^/]+)+|localhost)$/;
32
33// For hostname with colon. eg: ssh://user@github.com:foo/bar
34const RE_HOSTNAME_WITH_COLON =
35 /([^/@#:.]+(?:\.[^/@#:.]+)+|localhost):([^#/0-9]+)/;
36
37// Reg for url without protocol
38const RE_NO_PROTOCOL = /^([^/@#:.]+(?:\.[^/@#:.]+)+)/;
39
40// RegExp for version string
41const VERSION_PATTERN_REGEXP = /^([\d^=v<>~]|[*xX]$)/;
42
43// Specific protocol for short url without normal hostname
44const PROTOCOLS_FOR_SHORT = [
45 "github:",
46 "gitlab:",
47 "bitbucket:",
48 "gist:",
49 "file:"
50];
51
52// Default protocol for git url
53const DEF_GIT_PROTOCOL = "git+ssh://";
54
55// thanks to https://github.com/npm/hosted-git-info/blob/latest/git-host-info.js
56const extractCommithashByDomain = {
57 /**
58 * @param {string} pathname pathname
59 * @param {string} hash hash
60 * @returns {string | undefined} hash
61 */
62 "github.com": (pathname, hash) => {
63 let [, user, project, type, commithash] = pathname.split("/", 5);
64 if (type && type !== "tree") {
65 return;
66 }
67
68 commithash = !type ? hash : `#${commithash}`;
69
70 if (project && project.endsWith(".git")) {
71 project = project.slice(0, -4);
72 }
73
74 if (!user || !project) {
75 return;
76 }
77
78 return commithash;
79 },
80 /**
81 * @param {string} pathname pathname
82 * @param {string} hash hash
83 * @returns {string | undefined} hash
84 */
85 "gitlab.com": (pathname, hash) => {
86 const path = pathname.slice(1);
87 if (path.includes("/-/") || path.includes("/archive.tar.gz")) {
88 return;
89 }
90
91 const segments = path.split("/");
92 let project = /** @type {string} */ (segments.pop());
93 if (project.endsWith(".git")) {
94 project = project.slice(0, -4);
95 }
96
97 const user = segments.join("/");
98 if (!user || !project) {
99 return;
100 }
101
102 return hash;
103 },
104 /**
105 * @param {string} pathname pathname
106 * @param {string} hash hash
107 * @returns {string | undefined} hash
108 */
109 "bitbucket.org": (pathname, hash) => {
110 let [, user, project, aux] = pathname.split("/", 4);
111 if (["get"].includes(aux)) {
112 return;
113 }
114
115 if (project && project.endsWith(".git")) {
116 project = project.slice(0, -4);
117 }
118
119 if (!user || !project) {
120 return;
121 }
122
123 return hash;
124 },
125 /**
126 * @param {string} pathname pathname
127 * @param {string} hash hash
128 * @returns {string | undefined} hash
129 */
130 "gist.github.com": (pathname, hash) => {
131 let [, user, project, aux] = pathname.split("/", 4);
132 if (aux === "raw") {
133 return;
134 }
135
136 if (!project) {
137 if (!user) {
138 return;
139 }
140
141 project = user;
142 }
143
144 if (project.endsWith(".git")) {
145 project = project.slice(0, -4);
146 }
147
148 return hash;
149 }
150};
151
152/**
153 * extract commit hash from parsed url
154 * @inner
155 * @param {URL} urlParsed parsed url
156 * @returns {string} commithash
157 */
158function getCommithash(urlParsed) {
159 let { hostname, pathname, hash } = urlParsed;
160 hostname = hostname.replace(/^www\./, "");
161
162 try {
163 hash = decodeURIComponent(hash);
164 // eslint-disable-next-line no-empty
165 } catch (_err) {}
166
167 if (
168 extractCommithashByDomain[
169 /** @type {keyof extractCommithashByDomain} */ (hostname)
170 ]
171 ) {
172 return (
173 extractCommithashByDomain[
174 /** @type {keyof extractCommithashByDomain} */ (hostname)
175 ](pathname, hash) || ""
176 );
177 }
178
179 return hash;
180}
181
182/**
183 * make url right for URL parse
184 * @inner
185 * @param {string} gitUrl git url
186 * @returns {string} fixed url
187 */
188function correctUrl(gitUrl) {
189 // like:
190 // proto://hostname.com:user/repo -> proto://hostname.com/user/repo
191 return gitUrl.replace(RE_HOSTNAME_WITH_COLON, "$1/$2");
192}
193
194/**
195 * make url protocol right for URL parse
196 * @inner
197 * @param {string} gitUrl git url
198 * @returns {string} fixed url
199 */
200function correctProtocol(gitUrl) {
201 // eg: github:foo/bar#v1.0. Should not add double slash, in case of error parsed `pathname`
202 if (RE_GIT_URL_SHORT.test(gitUrl)) {
203 return gitUrl;
204 }
205
206 // eg: user@github.com:foo/bar
207 if (!RE_CUSTOM_PROTOCOL.test(gitUrl)) {
208 return `${DEF_GIT_PROTOCOL}${gitUrl}`;
209 }
210
211 return gitUrl;
212}
213
214/**
215 * extract git dep version from hash
216 * @inner
217 * @param {string} hash hash
218 * @returns {string} git dep version
219 */
220function getVersionFromHash(hash) {
221 const matched = hash.match(RE_URL_HASH_VERSION);
222
223 return (matched && matched[1]) || "";
224}
225
226/**
227 * if string can be decoded
228 * @inner
229 * @param {string} str str to be checked
230 * @returns {boolean} if can be decoded
231 */
232function canBeDecoded(str) {
233 try {
234 decodeURIComponent(str);
235 } catch (_err) {
236 return false;
237 }
238
239 return true;
240}
241
242/**
243 * get right dep version from git url
244 * @inner
245 * @param {string} gitUrl git url
246 * @returns {string} dep version
247 */
248function getGitUrlVersion(gitUrl) {
249 const oriGitUrl = gitUrl;
250 // github extreme shorthand
251 gitUrl = RE_URL_GITHUB_EXTREME_SHORT.test(gitUrl)
252 ? `github:${gitUrl}`
253 : correctProtocol(gitUrl);
254
255 gitUrl = correctUrl(gitUrl);
256
257 let parsed;
258 try {
259 parsed = new URL(gitUrl);
260 // eslint-disable-next-line no-empty
261 } catch (_err) {}
262
263 if (!parsed) {
264 return "";
265 }
266
267 const { protocol, hostname, pathname, username, password } = parsed;
268 if (!RE_PROTOCOL.test(protocol)) {
269 return "";
270 }
271
272 // pathname shouldn't be empty or URL malformed
273 if (!pathname || !canBeDecoded(pathname)) {
274 return "";
275 }
276
277 // without protocol, there should have auth info
278 if (RE_NO_PROTOCOL.test(oriGitUrl) && !username && !password) {
279 return "";
280 }
281
282 if (!PROTOCOLS_FOR_SHORT.includes(protocol.toLowerCase())) {
283 if (!RE_HOSTNAME.test(hostname)) {
284 return "";
285 }
286
287 const commithash = getCommithash(parsed);
288 return getVersionFromHash(commithash) || commithash;
289 }
290
291 // for protocol short
292 return getVersionFromHash(gitUrl);
293}
294
295/**
296 * @param {string} str maybe required version
297 * @returns {boolean} true, if it looks like a version
298 */
299function isRequiredVersion(str) {
300 return VERSION_PATTERN_REGEXP.test(str);
301}
302
303module.exports.isRequiredVersion = isRequiredVersion;
304
305/**
306 * @see https://docs.npmjs.com/cli/v7/configuring-npm/package-json#urls-as-dependencies
307 * @param {string} versionDesc version to be normalized
308 * @returns {string} normalized version
309 */
310function normalizeVersion(versionDesc) {
311 versionDesc = (versionDesc && versionDesc.trim()) || "";
312
313 if (isRequiredVersion(versionDesc)) {
314 return versionDesc;
315 }
316
317 // add handle for URL Dependencies
318 return getGitUrlVersion(versionDesc.toLowerCase());
319}
320
321module.exports.normalizeVersion = normalizeVersion;
322
323/** @typedef {{ data: JsonObject, path: string }} DescriptionFile */
324
325/**
326 * @param {InputFileSystem} fs file system
327 * @param {string} directory directory to start looking into
328 * @param {string[]} descriptionFiles possible description filenames
329 * @param {function((Error | null)=, DescriptionFile=, string[]=): void} callback callback
330 * @param {function(DescriptionFile=): boolean} satisfiesDescriptionFileData file data compliance check
331 * @param {Set<string>} checkedFilePaths set of file paths that have been checked
332 */
333const getDescriptionFile = (
334 fs,
335 directory,
336 descriptionFiles,
337 callback,
338 satisfiesDescriptionFileData,
339 checkedFilePaths = new Set()
340) => {
341 let i = 0;
342
343 const satisfiesDescriptionFileDataInternal = {
344 check: satisfiesDescriptionFileData,
345 checkedFilePaths
346 };
347
348 const tryLoadCurrent = () => {
349 if (i >= descriptionFiles.length) {
350 const parentDirectory = dirname(fs, directory);
351 if (!parentDirectory || parentDirectory === directory) {
352 return callback(
353 null,
354 undefined,
355 Array.from(satisfiesDescriptionFileDataInternal.checkedFilePaths)
356 );
357 }
358 return getDescriptionFile(
359 fs,
360 parentDirectory,
361 descriptionFiles,
362 callback,
363 satisfiesDescriptionFileDataInternal.check,
364 satisfiesDescriptionFileDataInternal.checkedFilePaths
365 );
366 }
367 const filePath = join(fs, directory, descriptionFiles[i]);
368 readJson(fs, filePath, (err, data) => {
369 if (err) {
370 if ("code" in err && err.code === "ENOENT") {
371 i++;
372 return tryLoadCurrent();
373 }
374 return callback(err);
375 }
376 if (!data || typeof data !== "object" || Array.isArray(data)) {
377 return callback(
378 new Error(`Description file ${filePath} is not an object`)
379 );
380 }
381 if (
382 typeof satisfiesDescriptionFileDataInternal.check === "function" &&
383 !satisfiesDescriptionFileDataInternal.check({ data, path: filePath })
384 ) {
385 i++;
386 satisfiesDescriptionFileDataInternal.checkedFilePaths.add(filePath);
387 return tryLoadCurrent();
388 }
389 callback(null, { data, path: filePath });
390 });
391 };
392 tryLoadCurrent();
393};
394module.exports.getDescriptionFile = getDescriptionFile;
395
396/**
397 * @param {JsonObject} data description file data i.e.: package.json
398 * @param {string} packageName name of the dependency
399 * @returns {string | undefined} normalized version
400 */
401const getRequiredVersionFromDescriptionFile = (data, packageName) => {
402 const dependencyTypes = [
403 "optionalDependencies",
404 "dependencies",
405 "peerDependencies",
406 "devDependencies"
407 ];
408
409 for (const dependencyType of dependencyTypes) {
410 const dependency = /** @type {JsonObject} */ (data[dependencyType]);
411 if (
412 dependency &&
413 typeof dependency === "object" &&
414 packageName in dependency
415 ) {
416 return normalizeVersion(
417 /** @type {Exclude<JsonPrimitive, null | boolean| number>} */ (
418 dependency[packageName]
419 )
420 );
421 }
422 }
423};
424module.exports.getRequiredVersionFromDescriptionFile =
425 getRequiredVersionFromDescriptionFile;
Note: See TracBrowser for help on using the repository browser.