UNPKG

4.26 kBJavaScriptView Raw
1const fs = require('fs');
2const path = require('path');
3const fetch = require('node-fetch');
4const crypto = require('crypto');
5const {ensureParsed} = require('ast-matcher');
6const convert = require('convert-source-map');
7const url = require('url');
8require('./ensure-parser-set')();
9
10const jsExtentions = ['.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs'];
11
12exports.stripJsExtension = function(d) {
13 const ext = path.extname(d || '');
14 if (jsExtentions.includes(ext)) {
15 return d.slice(0, - ext.length);
16 }
17 return d;
18};
19
20exports.isPackageName = function(path) {
21 if (path.startsWith('.')) return false;
22 const parts = path.split('/');
23 // package name, or scope package name
24 return parts.length === 1 || (parts.length === 2 && parts[0].startsWith('@'));
25};
26
27function fsReadFile(filePath) {
28 return new Promise((resolve, reject) => {
29 fs.readFile(filePath, (err, data) => {
30 if (err) reject(err);
31 resolve(data);
32 });
33 })
34}
35
36exports.fsReadFile = fsReadFile;
37
38function fsExists(filePath) {
39 return new Promise(resolve => {
40 fs.stat(filePath, (err, stats) => {
41 if (err) resolve(false);
42 else resolve(stats.isFile());
43 });
44 });
45}
46
47exports.fsExists = fsExists;
48
49exports.contentOrFile = function(pathOrContent, mock) {
50 // decoupling for testing
51 let _readFile = (mock && mock.readFile) || fsReadFile;
52
53 if (typeof pathOrContent !== 'string' || !pathOrContent) {
54 return Promise.reject(new Error('No content or file provided'));
55 }
56
57 // pathOrContent is a path
58 if (pathOrContent.match(/^https?:\/\//)) {
59 // remote url
60 const remote = url.parse(pathOrContent);
61 const remotePath = remote.hostname + remote.pathname;
62
63 return fetch(pathOrContent)
64 .then(response => {
65 if (response.ok) return response.text();
66 else throw new Error(response.statusText)
67 })
68 .then(text => {
69 ensureParsed(text);
70 // pathOrContent is code
71 return text;
72 })
73 .then(text => ({
74 path: remotePath,
75 contents: ensureSemicolon(stripSourceMappingUrl(text || '')),
76 }));
77 }
78
79 if (pathOrContent.endsWith('.js')) {
80 return _readFile(pathOrContent)
81 .then(buffer => buffer.toString())
82 .then(text => ({
83 path: pathOrContent.replace(/\\/g, '/'),
84 contents: ensureSemicolon(stripSourceMappingUrl(text || '')),
85 sourceMap: getSourceMap(text, pathOrContent)
86 }));
87 }
88
89 return new Promise(resolve => {
90 ensureParsed(pathOrContent);
91 resolve({
92 contents: ensureSemicolon(stripSourceMappingUrl(pathOrContent || ''))
93 });
94 });
95};
96
97exports.generateHash = function(bufOrStr) {
98 return crypto.createHash('md5').update(bufOrStr).digest('hex');
99};
100
101function stripSourceMappingUrl(contents) {
102 return convert.removeMapFileComments(convert.removeComments(contents));
103}
104
105exports.stripSourceMappingUrl = stripSourceMappingUrl;
106
107function getSourceMap(contents, filePath) {
108 const dir = (filePath && path.dirname(filePath)) || '';
109
110 const sourceMap = (() => {
111 try {
112 let converter = convert.fromSource(contents);
113 if (converter) return converter.sourcemap;
114
115 if (filePath) {
116 converter = convert.fromMapFileSource(contents, dir);
117 if (converter) return converter.sourcemap;
118 }
119 } catch (err) {
120 return;
121 }
122 })();
123
124 if (sourceMap && sourceMap.sources) {
125 const {sourceRoot} = sourceMap;
126 if (sourceRoot) {
127 // get rid of sourceRoot
128 if (sourceRoot !== '/') {
129 sourceMap.sources = sourceMap.sources.map(s => path.join(sourceRoot, s).replace(/\\/g, '/'));
130 }
131 delete sourceMap.sourceRoot;
132 }
133
134 sourceMap.sources = sourceMap.sources.map(s => path.join(dir, s).replace(/\\/g, '/'));
135 if (filePath) {
136 sourceMap.file = filePath.replace(/\\/g, '/');
137 }
138
139 if (!sourceMap.sourcesContent) {
140 // bring in sources content inline
141 try {
142 sourceMap.sourcesContent = sourceMap.sources.map(s =>
143 fs.readFileSync(s, 'utf8')
144 );
145 } catch (err) {
146 //
147 }
148 }
149 }
150
151 return sourceMap;
152}
153
154exports.getSourceMap = getSourceMap;
155
156function ensureSemicolon(contents) {
157 let trimed = contents.trim();
158 if (trimed.slice(-1) === ';') return trimed;
159 return trimed + ';';
160}
161
162exports.ensureSemicolon = ensureSemicolon;