create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
264 lines (234 loc) • 6.65 kB
Flow
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const B64Builder = require('./B64Builder');
import type {
BasicSourceMap,
FBSourceMetadata,
FBSourceFunctionMap,
} from './source-map';
/**
* Generates a source map from raw mappings.
*
* Raw mappings are a set of 2, 4, or five elements:
*
* - line and column number in the generated source
* - line and column number in the original source
* - symbol name in the original source
*
* Mappings have to be passed in the order appearance in the generated source.
*/
class Generator {
builder: B64Builder;
last: {|
generatedColumn: number,
generatedLine: number,
name: number,
source: number,
sourceColumn: number,
sourceLine: number,
|};
names: IndexedSet;
source: number;
sources: Array<string>;
sourcesContent: Array<?string>;
x_facebook_sources: Array<?FBSourceMetadata>;
constructor() {
this.builder = new B64Builder();
this.last = {
generatedColumn: 0,
generatedLine: 1, // lines are passed in 1-indexed
name: 0,
source: 0,
sourceColumn: 0,
sourceLine: 1,
};
this.names = new IndexedSet();
this.source = -1;
this.sources = [];
this.sourcesContent = [];
this.x_facebook_sources = [];
}
/**
* Mark the beginning of a new source file.
*/
startFile(file: string, code: string, functionMap: ?FBSourceFunctionMap) {
this.source = this.sources.push(file) - 1;
this.sourcesContent.push(code);
this.x_facebook_sources.push(functionMap ? [functionMap] : null);
}
/**
* Mark the end of the current source file
*/
endFile() {
this.source = -1;
}
/**
* Adds a mapping for generated code without a corresponding source location.
*/
addSimpleMapping(generatedLine: number, generatedColumn: number): void {
const last = this.last;
if (
this.source === -1 ||
(generatedLine === last.generatedLine &&
generatedColumn < last.generatedColumn) ||
generatedLine < last.generatedLine
) {
const msg =
this.source === -1
? 'Cannot add mapping before starting a file with `addFile()`'
: 'Mapping is for a position preceding an earlier mapping';
throw new Error(msg);
}
if (generatedLine > last.generatedLine) {
this.builder.markLines(generatedLine - last.generatedLine);
last.generatedLine = generatedLine;
last.generatedColumn = 0;
}
this.builder.startSegment(generatedColumn - last.generatedColumn);
last.generatedColumn = generatedColumn;
}
/**
* Adds a mapping for generated code with a corresponding source location.
*/
addSourceMapping(
generatedLine: number,
generatedColumn: number,
sourceLine: number,
sourceColumn: number,
): void {
this.addSimpleMapping(generatedLine, generatedColumn);
const last = this.last;
this.builder
.append(this.source - last.source)
.append(sourceLine - last.sourceLine)
.append(sourceColumn - last.sourceColumn);
last.source = this.source;
last.sourceColumn = sourceColumn;
last.sourceLine = sourceLine;
}
/**
* Adds a mapping for code with a corresponding source location + symbol name.
*/
addNamedSourceMapping(
generatedLine: number,
generatedColumn: number,
sourceLine: number,
sourceColumn: number,
name: string,
): void {
this.addSourceMapping(
generatedLine,
generatedColumn,
sourceLine,
sourceColumn,
);
const last = this.last;
const nameIndex = this.names.indexFor(name);
this.builder.append(nameIndex - last.name);
last.name = nameIndex;
}
/**
* Return the source map as object.
*/
toMap(
file?: string,
options?: {excludeSource?: boolean, ...},
): BasicSourceMap {
let content, sourcesMetadata;
if (options && options.excludeSource) {
content = {};
} else {
content = {sourcesContent: this.sourcesContent.slice()};
}
if (this.hasSourcesMetadata()) {
sourcesMetadata = {
x_facebook_sources: JSON.parse(JSON.stringify(this.x_facebook_sources)),
};
} else {
sourcesMetadata = {};
}
/* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.111 was deployed. To see the error, delete this
* comment and run Flow. */
return {
version: 3,
file,
sources: this.sources.slice(),
...content,
/* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.111 was deployed. To see the error, delete
* this comment and run Flow. */
...sourcesMetadata,
names: this.names.items(),
mappings: this.builder.toString(),
};
}
/**
* Return the source map as string.
*
* This is ~2.5x faster than calling `JSON.stringify(generator.toMap())`
*/
toString(file?: string, options?: {excludeSource?: boolean, ...}): string {
let content, sourcesMetadata;
if (options && options.excludeSource) {
content = '';
} else {
content = `"sourcesContent":${JSON.stringify(this.sourcesContent)},`;
}
if (this.hasSourcesMetadata()) {
sourcesMetadata = `"x_facebook_sources":${JSON.stringify(
this.x_facebook_sources,
)},`;
} else {
sourcesMetadata = '';
}
return (
'{' +
'"version":3,' +
(file ? `"file":${JSON.stringify(file)},` : '') +
`"sources":${JSON.stringify(this.sources)},` +
content +
sourcesMetadata +
`"names":${JSON.stringify(this.names.items())},` +
`"mappings":"${this.builder.toString()}"` +
'}'
);
}
/**
* Determine whether we need to write the `x_facebook_sources` field.
* If the metadata is all `null`s, we can omit the field entirely.
*/
hasSourcesMetadata(): boolean {
return this.x_facebook_sources.some(
metadata => metadata != null && metadata.some(value => value != null),
);
}
}
class IndexedSet {
map: Map<string, number>;
nextIndex: number;
constructor() {
this.map = new Map();
this.nextIndex = 0;
}
indexFor(x: string): number {
let index = this.map.get(x);
if (index == null) {
index = this.nextIndex++;
this.map.set(x, index);
}
return index;
}
items(): Array<string> {
return Array.from(this.map.keys());
}
}
module.exports = Generator;