Compare commits

...

5 Commits

Author SHA1 Message Date
Gal Schlezinger
f88a968438 remove path.resolve 2022-05-10 10:20:28 +03:00
Gal Schlezinger
67ea370642 Update packages/sourcemapped/README.md
Co-authored-by: Nathan Rajlich <n@n8.io>
2022-05-10 10:19:33 +03:00
Gal Schlezinger
d2daf28f13 Add a README 2022-05-09 18:31:42 +03:00
Gal Schlezinger
dc91dbd034 add files to package.json 2022-05-09 18:15:24 +03:00
Gal Schlezinger
77908f965c add sourcemapped module 2022-05-09 18:08:55 +03:00
10 changed files with 281 additions and 7 deletions

View File

@@ -0,0 +1,57 @@
# `sourcemapped`
Helpers and utilities to work with source maps.
### `fileToSource(contents: string, sourceName: string, sourcePath?: string): Promise<Source>`
This function takes a `content`, a `sourceName` and an optional `sourcePath` and returns a source map-aware `Source` object. It will first try to load `${sourcePath}.map`, and if it is missing, it will try to load a source map from the given `content`.
```ts
import { sourcemapped, fileToSource } from 'sourcemapped';
import fs from 'fs/promises';
const fullSourcePath = 'my-code-file.js';
const fileContents = await fs.readFile(fullSourcePath, 'utf8');
// Create a `Source` object for the file
const source = await fileToSource(
fileContents,
'my-code-file.js',
fullSourcePath
);
```
### `raw(content: string): Source`
The `raw` function returns a `Source` object with the given `content` with the source name `[native code]`.
### `stringifySourceMap(sourceMap): string`
Stringifies a given source map without the `sourcesContent` property.
### `sourcemapped` tagged template literals
This tagged template literal enables decorating a string while preserving source maps, as a drop-in replacement for a string literal, with source map support.
Each dynamic input of the template literal must be a valid `Source` object, so if you want to add an arbitrary value, make sure to cast it to a `Source` using `raw` or webpack-sources `OriginalSource` constructor.
```ts
import { sourcemapped, fileToSource } from 'sourcemapped';
import fs from 'fs/promises';
const fullSourcePath = 'my-code-file.js';
const fileContents = await fs.readFile(fullSourcePath, 'utf8');
// Create a `Source` object for the file
const source = await fileToSource(
fileContents,
'my-code-file.js',
fullSourcePath
);
const decoratedSource = sourcemapped`
console.log('before user code');
${source}
console.log('after user code');
`;
```

View File

@@ -0,0 +1,27 @@
{
"name": "sourcemapped",
"version": "1.0.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"files": [
"dist"
],
"license": "MIT",
"scripts": {
"build": "yarn build:cjs && yarn build:esm",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build:esm": "tsc -p tsconfig.esm.json"
},
"devDependencies": {
"@types/convert-source-map": "1.5.2",
"@types/source-map": "0.5.7",
"@types/webpack-sources": "3.2.0",
"source-map": "0.7.3",
"typescript": "4.6.4"
},
"dependencies": {
"convert-source-map": "1.8.0",
"webpack-sources": "3.2.3"
}
}

View File

@@ -0,0 +1,55 @@
import type { RawSourceMap } from 'source-map';
import convertSourceMap from 'convert-source-map';
import type { Source } from 'webpack-sources';
import { OriginalSource, SourceMapSource } from 'webpack-sources';
import { promises as fs, constants as fsConstants } from 'fs';
/**
* Takes a file with contents and tries to extract its source maps it will
* first try to use a `${fullFilePath}.map` file if it exists. Then, it will
* try to use the inline source map comment.
*
* @param content The file contents.
* @param sourceName the name of the source.
* @param fullFilePath The full path to the file.
*/
export async function fileToSource(
content: string,
sourceName: string,
fullFilePath?: string
): Promise<Source> {
const sourcemap = await getSourceMap(content, fullFilePath);
const cleanContent = convertSourceMap.removeComments(content);
return sourcemap
? new SourceMapSource(cleanContent, sourceName, sourcemap)
: new OriginalSource(cleanContent, sourceName);
}
/**
* Finds a source map for a given content and file path. First it will try to
* use a `${fullFilePath}.map` file if it exists. Then, it will try to use
* the inline source map comment.
*/
async function getSourceMap(
content: string,
fullFilePath?: string
): Promise<RawSourceMap | null> {
try {
if (fullFilePath && (await pathExists(`${fullFilePath}.map`))) {
const mapJson = await fs.readFile(`${fullFilePath}.map`, 'utf8');
return convertSourceMap.fromJSON(mapJson).toObject();
}
return convertSourceMap.fromComment(content).toObject();
} catch {
return null;
}
}
async function pathExists(path: string): Promise<boolean> {
try {
await fs.access(path, fsConstants.R_OK);
return true;
} catch {
return false;
}
}

View File

@@ -0,0 +1,3 @@
export { sourcemapped, raw } from './sourcemapped';
export { stringifySourceMap } from './stringifySourceMap';
export { fileToSource } from './fileToSource';

View File

@@ -0,0 +1,37 @@
import type { Source } from 'webpack-sources';
import { ConcatSource, OriginalSource } from 'webpack-sources';
/**
* A template literal tag that preserves existing source maps, if any. This
* allows to compose multiple sources and preserve the source maps, so we can
* resolve the correct line numbers in the stack traces later on.
*
* @param strings The string literals.
* @param sources All the sources that may optionally have source maps. Use
* `raw` to pass a string that should be inserted raw (with no source map
* attached).
*/
export function sourcemapped(
strings: TemplateStringsArray,
...sources: Source[]
): Source {
const concat = new ConcatSource();
for (let i = 0; i < Math.max(strings.length, sources.length); i++) {
const string = strings[i];
const source = sources[i];
if (string) concat.add(raw(string));
if (source) concat.add(source);
}
return concat;
}
/**
* A helper to create a Source from a string with no source map.
* This allows to obfuscate the source code from the user and print `[native code]`
* when resolving the stack trace.
*/
export function raw(value: string) {
return new OriginalSource(value, '[native code]');
}

View File

@@ -0,0 +1,18 @@
import type { RawSourceMap } from 'source-map';
import convertSourceMap from 'convert-source-map';
/**
* Stringifies a source map, removing unnecessary data:
* * `sourcesContent` is not needed to trace back frames.
*/
export function stringifySourceMap(
sourceMap?: RawSourceMap | string | null
): string | undefined {
if (!sourceMap) return;
const obj =
typeof sourceMap === 'object'
? { ...sourceMap }
: convertSourceMap.fromJSON(sourceMap).toObject();
delete obj.sourcesContent;
return JSON.stringify(obj);
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"noEmit": false,
"module": "commonjs",
"outDir": "dist/cjs"
}
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"noEmit": false,
"module": "ESNext",
"outDir": "dist/esm"
}
}

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"noEmit": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"lib": ["esnext"],
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": false,
"declaration": false
},
"include": ["src/**/*", "test/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -2149,6 +2149,11 @@
resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.3.tgz#3688bd77fc12f935548eef102a4e34c512b03a07"
integrity sha512-pv8VcFrZ3fN93L4rTNIbbUzdkzjEyVMp5mPVjsFfOYTDOZMZiZ8P1dhu+kEv3faYyKzZgLlSvnyQNFg+p/v5ug==
"@types/convert-source-map@1.5.2":
version "1.5.2"
resolved "https://registry.yarnpkg.com/@types/convert-source-map/-/convert-source-map-1.5.2.tgz#318dc22d476632a4855594c16970c6dc3ed086e7"
integrity sha512-tHs++ZeXer40kCF2JpE51Hg7t4HPa18B1b1Dzy96S0eCw8QKECNMYMfwa1edK/x8yCN0r4e6ewvLcc5CsVGkdg==
"@types/cookie@0.3.3":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
@@ -2542,6 +2547,18 @@
"@types/mime" "^1"
"@types/node" "*"
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
"@types/source-map@0.5.7":
version "0.5.7"
resolved "https://registry.yarnpkg.com/@types/source-map/-/source-map-0.5.7.tgz#165eeb583c1ef00196fe4ef4da5d7832b03b275b"
integrity sha512-LrnsgZIfJaysFkv9rRJp4/uAyqw87oVed3s1hhF83nwbo9c7MG9g5DqR0seHP+lkX4ldmMrVolPjQSe2ZfD0yA==
dependencies:
source-map "*"
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
@@ -2599,6 +2616,15 @@
"@types/configstore" "*"
boxen "^4.2.0"
"@types/webpack-sources@3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==
dependencies:
"@types/node" "*"
"@types/source-list-map" "*"
source-map "^0.7.3"
"@types/which@1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf"
@@ -4337,6 +4363,13 @@ conventional-recommended-bump@^5.0.0:
meow "^4.0.0"
q "^1.5.1"
convert-source-map@1.8.0, convert-source-map@^1.4.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
dependencies:
safe-buffer "~5.1.1"
convert-source-map@^1.1.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
@@ -4344,13 +4377,6 @@ convert-source-map@^1.1.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
dependencies:
safe-buffer "~5.1.1"
convert-source-map@^1.4.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
dependencies:
safe-buffer "~5.1.1"
convert-to-spaces@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715"
@@ -10493,6 +10519,11 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@*, source-map@0.7.3, source-map@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -11496,6 +11527,11 @@ typescript@4.3.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==
typescript@4.6.4:
version "4.6.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9"
integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==
uglify-js@^3.1.4:
version "3.13.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113"
@@ -11802,6 +11838,11 @@ webidl-conversions@^4.0.2:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
webpack-sources@3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
well-known-symbols@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5"