Skip to content

Commit

Permalink
refactor: code
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed May 25, 2020
1 parent 2ceba27 commit 0d77b18
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 363 deletions.
237 changes: 81 additions & 156 deletions src/index.js
@@ -1,27 +1,14 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
import path from 'path';

import { promisify } from 'util';

import validateOptions from 'schema-utils';
import parseDataURL from 'data-urls';

import { SourceMapConsumer } from 'source-map';

import { labelToName, decode } from 'whatwg-encoding';
import { getOptions, urlToRequest } from 'loader-utils';
import { getOptions } from 'loader-utils';

import schema from './options.json';
import {
flattenSourceMap,
readFile,
getContentFromSourcesContent,
getSourceMappingUrl,
getRequestedUrl,
} from './utils';
import { getSourceMappingURL, fetchFromURL, flattenSourceMap } from './utils';

export default async function loader(input, inputMap) {
const options = getOptions(this);
Expand All @@ -31,179 +18,117 @@ export default async function loader(input, inputMap) {
baseDataPath: 'options',
});

let { url } = getSourceMappingUrl(input);
const { replacementString } = getSourceMappingUrl(input);
const { sourceMappingURL, replacementString } = getSourceMappingURL(input);
const callback = this.async();

if (!url) {
if (!sourceMappingURL) {
callback(null, input, inputMap);

return;
}

const { fs, context, resolve, addDependency, emitWarning } = this;
const resolver = promisify(resolve);
const reader = promisify(fs.readFile).bind(fs);
let sourceURL;
let sourceContent;

if (url.toLowerCase().startsWith('data:')) {
const dataURL = parseDataURL(url);

if (dataURL) {
let map;

try {
dataURL.encodingName =
labelToName(dataURL.mimeType.parameters.get('charset')) || 'UTF-8';

map = decode(dataURL.body, dataURL.encodingName);
map = JSON.parse(map.replace(/^\)\]\}'/, ''));
} catch (error) {
emitWarning(
`Cannot parse inline SourceMap with Charset ${dataURL.encodingName}: ${error}`
);
try {
({ sourceURL, sourceContent } = await fetchFromURL(
this,
this.context,
sourceMappingURL
));
} catch (error) {
this.emitWarning(error);

callback(null, input, inputMap);
callback(null, input, inputMap);

return;
}
return;
}

processMap(map, context, callback);
if (sourceURL) {
this.addDependency(sourceURL);
}

return;
}
let map;

emitWarning(`Cannot parse inline SourceMap: ${url}`);
try {
map = JSON.parse(sourceContent.replace(/^\)\]\}'/, ''));
} catch (parseError) {
this.emitWarning(
new Error(`Cannot parse source map from '${sourceURL}': ${parseError}`)
);

callback(null, input, inputMap);

return;
}

try {
url = getRequestedUrl(url);
} catch (error) {
emitWarning(error.message);
const context = sourceURL ? path.dirname(sourceURL) : this.context;

callback(null, input, inputMap);

return;
if (map.sections) {
// eslint-disable-next-line no-param-reassign
map = await flattenSourceMap(map);
}

let urlResolved;
const resolvedSources = await Promise.all(
map.sources.map(async (source, i) => {
// eslint-disable-next-line no-shadow
let sourceURL;
// eslint-disable-next-line no-shadow
let sourceContent;

try {
urlResolved = await resolver(context, urlToRequest(url, true));
} catch (resolveError) {
emitWarning(`Cannot find SourceMap '${url}': ${resolveError}`);
const originalSourceContent =
map.sourcesContent && map.sourcesContent[i]
? map.sourcesContent[i]
: null;
const skipReading = originalSourceContent !== null;

callback(null, input, inputMap);
try {
({ sourceURL, sourceContent } = await fetchFromURL(
this,
context,
source,
map.sourceRoot,
skipReading
));
} catch (error) {
this.emitWarning(error);

return;
}
sourceURL = source;
}

urlResolved = urlResolved.toString();
if (originalSourceContent) {
sourceContent = originalSourceContent;
}

addDependency(urlResolved);
if (sourceURL) {
this.addDependency(sourceURL);
}

const content = await reader(urlResolved);
let map;
return { sourceURL, sourceContent };
})
);

try {
map = JSON.parse(content.toString());
} catch (parseError) {
emitWarning(`Cannot parse SourceMap '${url}': ${parseError}`);
const newMap = { ...map };

callback(null, input, inputMap);
newMap.sources = [];
newMap.sourcesContent = [];

return;
}
delete newMap.sourceRoot;

processMap(map, path.dirname(urlResolved), callback);

// eslint-disable-next-line no-shadow
async function processMap(map, context, callback) {
if (map.sections) {
// eslint-disable-next-line no-param-reassign
map = await flattenSourceMap(map);
}

const mapConsumer = await new SourceMapConsumer(map);

let resolvedSources;

try {
resolvedSources = await Promise.all(
map.sources.map(async (source) => {
const fullPath = map.sourceRoot
? `${map.sourceRoot}${path.sep}${source}`
: source;

const originalData = getContentFromSourcesContent(
mapConsumer,
source
);

if (path.isAbsolute(fullPath)) {
return originalData
? { source: fullPath, content: originalData }
: readFile(fullPath, emitWarning, reader);
}

let fullPathResolved;

try {
fullPathResolved = await resolver(
context,
urlToRequest(fullPath, true)
);
} catch (resolveError) {
emitWarning(`Cannot find source file '${source}': ${resolveError}`);

return originalData
? {
source: fullPath,
content: originalData,
}
: { source: fullPath, content: null };
}

return originalData
? {
source: fullPathResolved,
content: originalData,
}
: readFile(fullPathResolved, emitWarning, reader);
})
);
} catch (error) {
emitWarning(error);

callback(null, input, inputMap);
}

const resultMap = { ...map };
resultMap.sources = [];
resultMap.sourcesContent = [];

delete resultMap.sourceRoot;

resolvedSources.forEach((res) => {
// eslint-disable-next-line no-param-reassign
resultMap.sources.push(path.normalize(res.source));
resultMap.sourcesContent.push(res.content);

if (res.source) {
addDependency(res.source);
}
});
resolvedSources.forEach((source) => {
// eslint-disable-next-line no-shadow
const { sourceURL, sourceContent } = source;

const sourcesContentIsEmpty =
resultMap.sourcesContent.filter((entry) => !!entry).length === 0;
newMap.sources.push(sourceURL || '');
newMap.sourcesContent.push(sourceContent || '');
});

if (sourcesContentIsEmpty) {
delete resultMap.sourcesContent;
}
const sourcesContentIsEmpty =
newMap.sourcesContent.filter((entry) => Boolean(entry)).length === 0;

callback(null, input.replace(replacementString, ''), resultMap);
if (sourcesContentIsEmpty) {
delete newMap.sourcesContent;
}

callback(null, input.replace(replacementString, ''), newMap);
}

0 comments on commit 0d77b18

Please sign in to comment.