Skip to content

Instantly share code, notes, and snippets.

@frogcjn
Last active March 18, 2018 12:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frogcjn/fb33fd721e0186cc70ab47994f5c77dc to your computer and use it in GitHub Desktop.
Save frogcjn/fb33fd721e0186cc70ab47994f5c77dc to your computer and use it in GitHub Desktop.
Transformer for debugging ReactNative with TypeScript file
"use strict"
const fs = require("fs")
const path = require("path")
const transformer = require("react-native/packager/transformer")
/*
1. put this file at your react native project workspace root,
2. then run command:
`react-native start --transformer ./transformer.js`
3. then use another terminal window to run command:
`react-native run-ios`
or
`react-native run-android`
4. use menu to choose remote debug with chrome
*/
// This function is from https://github.com/mjmeintjes/boot-react-native/blob/master/resources/mattsum/boot_rn/js/cljs-rn-transformer.js
function getBasicMappings(code) {
//TODO: This is copied from https://github.com/facebook/react-native/blob/528e30987aba8848f8c8815f00c42ecb2ce0919a/packager/react-packager/src/Bundler/Bundle.js#L265
//Therefore it is not working. But, this is only run for js files, and Chrome falls
//back to displaying the Javascript from the bundled file anyway when it can't understand
//the source mappings from here.
//TLDR - I don't think this does anything, and it probably needs to be fixed, but not critical.
var mappings = ""
const line = "AACA"
let lastCharNewLine = false
var moduleLines = 0
for (let t = 0; t < code.length; t++) {
if (t === 0) {
mappings += "AC"
mappings += "A"
} else if (lastCharNewLine) {
moduleLines++
mappings += line
}
lastCharNewLine = code[t] === "\n"
if (lastCharNewLine) {
mappings += ";"
}
}
mappings += ";"
return mappings
}
// This function is from https://github.com/mjmeintjes/boot-react-native/blob/master/resources/mattsum/boot_rn/js/cljs-rn-transformer.js
function getBasicSourceMap(filename, code) {
//This is supposed to just provide a fallback sourcemap
//If we don't provide a source map for every module, then Google Chrome
//starts throwing errors ('sources' could not be found on undefined)
//Chrome expects every section (and there is a section for each module) to
//have a 'map' property with an array of 'sources'.
var mappings = getBasicMappings(code)
const map = {
file: "bundle.js",
sources: [filename],
version: 3,
names: [],
mappings: mappings,
sourcesContent: [""]
}
return map
}
// most code are from
//https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/sourceMaps.ts
const SOURCE_MAPPING_MATCHER = new RegExp("//[#@] ?sourceMappingURL=(.+)$")
/**
* try to find the 'sourceMappingURL' in the file with the given path.
* Returns null in case of errors.
* https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/sourceMaps.ts
*/
function _findSourceMapUrlInFile(pathToGenerated, content) {
try {
const contents = content || fs.readFileSync(pathToGenerated).toString()
const lines = contents.split("\n")
for (let line of lines) {
const matches = SOURCE_MAPPING_MATCHER.exec(line)
if (matches && matches.length === 2) {
const uri = matches[1].trim()
return uri
}
}
} catch (_) {
// ignore exception
}
return null
}
/**
* Loads source map from file system.
* If no generatedPath is given, the 'file' attribute of the source map is used.
* https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/sourceMaps.ts
*/
function _loadSourceMap(map_path) {
try {
const mp = path.join(map_path)
const contents = fs.readFileSync(mp).toString()
return contents
} catch (_) {
// TODO
}
return null
}
/**
* Tries to find a SourceMap for the given path to a generated file.
* This is simple if the generated file has the 'sourceMappingURL' at the end.
* If not, we are using some heuristics...
* https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/sourceMaps.ts
*/
function _findGeneratedToSourceMapping(pathToGenerated, content) {
if (!pathToGenerated) {
return null
}
// try to find a data uri, which is inline sourcemap in generated file
let map_path = null
const uri = _findSourceMapUrlInFile(pathToGenerated, content)
if (uri) {
// if uri is data url source map is inlined in generated file
if (uri.indexOf("data:application/json") >= 0) {
const pos = uri.lastIndexOf(",")
if (pos > 0) {
const data = uri.substr(pos + 1)
try {
const buffer = new Buffer(data, "base64")
const json = buffer.toString()
if (json) {
return json
}
}catch (_) {
// TODO
}
}
} else {
map_path = uri
}
}
// try to find a valid url marked in generated file
if (map_path && !path.isAbsolute(map_path)) {
map_path = path.resolve(path.dirname(pathToGenerated), map_path)
}
// try to find map file next to the generated source
if (!map_path || !fs.existsSync(map_path)) {
map_path = pathToGenerated + ".map"
}
if (map_path && fs.existsSync(map_path)) {
const map = _loadSourceMap(map_path, pathToGenerated)
if (map) {
return map
}
}
return null
}
function getSourceMapFromCode(pathToSource) {
const mapContents = _findGeneratedToSourceMapping(pathToSource)
return mapContents ? JSON.parse(mapContents) : null
}
module.exports = function (data, callback) {
const filePath = data.filename
const fileContents = data.sourceCode
transformer(data, (error, result) => {
result.map = result.map || getSourceMapFromCode(filePath, fileContents) || getBasicSourceMap(filePath, fileContents)
return callback(error, result)
})
}
  1. put this file at your react native project workspace root,

  2. then run command: react-native start --transformer ./transformer.js

  3. then use another terminal window to run command: react-native run-ios or react-native run-android

  4. use menu to choose remote debug with chrome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment