// FormatMappedECMASource parses and formats the given ECMAScript source code. func FormatMappedECMASource(source string, sm *sourcemap.SourceMap) (string, *sourcemap.SourceMap, error) { // Parse the ES source. program, err := parser.ParseFile(nil, "", source, 0) if err != nil { return "", nil, err } // Reformat nicely. formatter := &sourceFormatter{ file: program.File, indentationLevel: 0, hasNewline: true, newlineCount: 0, charactersOnLine: 0, existingSourceMap: sm, formattedSourceMap: sourcemap.NewSourceMap(sm.GeneratedFilePath(), sm.SourceRoot()), positionMapper: compilercommon.CreateSourcePositionMapper([]byte(source)), } formatter.formatProgram(program) return formatter.buf.String(), formatter.formattedSourceMap, nil }
// GenerateES5 produces ES5 code from the given scope graph. func GenerateES5(sg *scopegraph.ScopeGraph, generatedFilePath string, sourceRoot string) (string, *sourcemap.SourceMap, error) { generated := generateModules(sg) // Order the modules by their paths. pather := shared.NewPather(sg.SourceGraph().Graph) modulePathMap := map[string]esbuilder.SourceBuilder{} var modulePathList = make([]string, 0) for module, _ := range generated { path := pather.GetModulePath(module) modulePathList = append(modulePathList, path) modulePathMap[path] = generated[module] } sort.Strings(modulePathList) // Collect the generated modules into their final source. ordered := ordered_map.NewOrderedMap() for _, modulePath := range modulePathList { ordered.Set(modulePath, modulePathMap[modulePath]) } // Generate the unformatted code and source map. template := esbuilder.Template("es5", runtimeTemplate, ordered) sm := sourcemap.NewSourceMap(generatedFilePath, sourceRoot) unformatted := esbuilder.BuildSourceAndMap(template, sm) // Format the code. return escommon.FormatMappedECMASource(unformatted.String(), sm) }
func (builder templateBuilder) emitSource(sb *sourceBuilder) { var maps []offsetedSourceMap = make([]offsetedSourceMap, 0) var source bytes.Buffer // Register an `emit` function which does two things: // // 1) Builds and emits the source for the node at the place the function is called // 2) Saves the node's source map at the current template location for later appending emitNode := func(node SourceBuilder) string { if node == nil { return "" } if sb.sourcemap != nil { currentIndex := source.Len() sm := sourcemap.NewSourceMap("", "") buf := BuildSourceAndMap(node, sm) maps = append(maps, offsetedSourceMap{currentIndex, sm}) return buf.String() } else { buf := BuildSource(node) return buf.String() } } funcMap := template.FuncMap{ "emit": emitNode, } t := template.New(builder.name).Funcs(funcMap) parsed, err := t.Parse(builder.templateSource) if err != nil { panic(err) } // Execute the template. eerr := parsed.Execute(&source, builder.data) if eerr != nil { panic(eerr) } // Append the generated source to the builder. generatedSource := source.String() sb.append(generatedSource) // Append any offsetted source mappings. if sb.sourcemap != nil { for _, osm := range maps { sb.sourcemap.AppendMap(osm.sourcemap.OffsetBy(generatedSource[0:osm.offset])) } } }
// Build performs the build of the source, writing the result to the response writer. func (dt *developTransaction) Build(w http.ResponseWriter, r *http.Request) { // Build a scope graph for the project. This will conduct parsing and type graph // construction on our behalf. scopeResult := scopegraph.ParseAndBuildScopeGraph(dt.rootSourceFilePath, dt.vcsDevelopmentDirectories, builder.CORE_LIBRARY) if !scopeResult.Status { dt.sourceMap = sourcemap.NewSourceMap(dt.name+".develop.js", "source/") for _, warning := range scopeResult.Warnings { dt.emitWarning(w, warning) } for _, err := range scopeResult.Errors { dt.emitError(w, err) } dt.emitInfo(w, "Build failed") dt.closeGroup(w) } else { // Generate the program's source. generated, sourceMap, err := es5.GenerateES5(scopeResult.Graph, dt.name+".develop.js", "source/") if err != nil { panic(err) } dt.sourceMap = sourceMap fmt.Fprint(w, generated) dt.emitInfo(w, "Build completed successfully") dt.closeGroup(w) dt.offsetCount = len(strings.Split(string(generated), "\n")) for _, warning := range scopeResult.Warnings { dt.emitWarning(w, warning) } } fmt.Fprintf(w, "//# sourceMappingURL=/%s.develop.js.map\n", dt.name) }
func TestGeneration(t *testing.T) { for _, test := range generationTests { sm := sourcemap.NewSourceMap("", "") buf := BuildSourceAndMap(test.node, sm) if !assert.Equal(t, buf.String(), test.expectedCode, "Mismatch on generation test %s", test.name) { continue } built := sm.Build() for _, expectedMapping := range test.expectedMappings { mapping, ok := built.LookupMapping(expectedMapping.lineNumber, expectedMapping.colPosition) if !assert.True(t, ok, "Expected mapping %v on test %s", expectedMapping, test.name) { continue } if !assert.Equal(t, expectedMapping.mapping, mapping, "Mapping mismatch on test %s", test.name) { continue } } } }
// FormatECMASource parses and formats the given ECMAScript source code. func FormatECMASource(source string) (string, error) { formatted, _, err := FormatMappedECMASource(source, sourcemap.NewSourceMap("", "")) return formatted, err }