/**
         * An IndexedSourceMapConsumer instance represents a parsed source map which
         * we can query for information. It differs from BasicSourceMapConsumer in
         * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
         * input.
         *
         * The only parameter is a raw source map (either as a JSON string, or already
         * parsed to an object). According to the spec for indexed source maps, they
         * have the following attributes:
         *
         *   - version: Which version of the source map spec this map is following.
         *   - file: Optional. The generated file this source map is associated with.
         *   - sections: A list of section definitions.
         *
         * Each value under the "sections" field has two fields:
         *   - offset: The offset into the original specified at which this section
         *       begins to apply, defined as an object with a "line" and "column"
         *       field.
         *   - map: A source map definition. This source map could also be indexed,
         *       but doesn't have to be.
         *
         * Instead of the "map" field, it's also possible to have a "url" field
         * specifying a URL to retrieve a source map from, but that's currently
         * unsupported.
         *
         * Here's an example source map, taken from the source map spec[0], but
         * modified to omit a section which uses the "url" field.
         *
         *  {
         *    version : 3,
         *    file: "app.js",
         *    sections: [{
         *      offset: {line:100, column:10},
         *      map: {
         *        version : 3,
         *        file: "section.js",
         *        sources: ["foo.js", "bar.js"],
         *        names: ["src", "maps", "are", "fun"],
         *        mappings: "AAAA,E;;ABCDE;"
         *      }
         *    }],
         *  }
         *
         * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
         */
        public IndexedSourceMapConsumer(SourceMapDescription sourceMapDescription)
        {
            var version  = sourceMapDescription.Version;
            var sections = sourceMapDescription.Sections;

            if (version != Version)
            {
                throw new Exception($"Unsupported version: {version}");
            }

            _sources = new ArraySet();
            _names   = new ArraySet();

            var lastOffset = new Offset
            {
                Line   = -1,
                Column = 0
            };

            _sections = sections.Select(s =>
            {
                if (s.Url != null)
                {
                    // The url field will require support for asynchronicity.
                    // See https://github.com/mozilla/source-map/issues/16
                    throw new NotImplementedException("Support for url field in sections not implemented.");
                }
                var offset       = s.Offset;
                var offsetLine   = s.Offset.Line;
                var offsetColumn = s.Offset.Column;

                if (offsetLine < lastOffset.Line ||
                    (offsetLine == lastOffset.Line && offsetColumn < lastOffset.Column))
                {
                    throw new Exception("Section offsets must be ordered and non-overlapping.");
                }
                lastOffset = offset;

                return(new GeneratedSection
                {
                    GeneratedOffset =
                        new GeneratedOffset
                    {
                        // The offset fields are 0-based, but we use 1-based indices when
                        // encoding/decoding from VLQ.
                        GeneratedLine = offsetLine + 1,
                        GeneratedColumn = offsetColumn + 1
                    },
                    Consumer = GetConsumer(s.Map)
                });
            }).ToList();
        }
Beispiel #2
0
        /**
         * A BasicSourceMapConsumer instance represents a parsed source map which we can
         * query for information about the original file positions by giving it a file
         * position in the generated source.
         *
         * The only parameter is the raw source map (either as a JSON string, or
         * already parsed to an object). According to the spec, source maps have the
         * following attributes:
         *
         *   - version: Which version of the source map spec this map is following.
         *   - sources: An array of URLs to the original source files.
         *   - names: An array of identifiers which can be referrenced by individual mappings.
         *   - sourceRoot: Optional. The URL root from which all sources are relative.
         *   - sourcesContent: Optional. An array of contents of the original source files.
         *   - mappings: A string of base64 VLQs which contain the actual mappings.
         *   - file: Optional. The generated file this source map is associated with.
         *
         * Here is an example source map, taken from the source map spec[0]:
         *
         *     {
         *       version : 3,
         *       file: "out.js",
         *       sourceRoot : "",
         *       sources: ["foo.js", "bar.js"],
         *       names: ["src", "maps", "are", "fun"],
         *       mappings: "AA,AB;;ABCDE;"
         *     }
         *
         * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
         */

        public BasicSourceMapConsumer(SourceMapDescription sourceMapDescription)
        {
            var sourceMapDescription1 = sourceMapDescription;

            int version = sourceMapDescription1.Version;

            string[] sources = sourceMapDescription1.Sources;
            // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
            // requires the array) to play nice here.
            var names          = sourceMapDescription1.Names ?? new string[0];
            var sourceRoot     = sourceMapDescription1.SourceRoot;
            var sourcesContent = sourceMapDescription1.SourcesContent;
            var mappings       = sourceMapDescription1.Mappings;

            // Once again, Sass deviates from the spec and supplies the version as a
            // string rather than a number, so we use loose equality checking here.
            if (version != Version)
            {
                throw new Exception($"Unsupported version: {version}");
            }

            sources = Enumerable.Select <string, string>(sources
                                                         // Some source maps produce relative source paths like "./foo.js" instead of
                                                         // "foo.js".  Normalize these first so that future comparisons will succeed.
                                                         // See bugzil.la/1090768.
                                                         .Select(Util.Normalize), source =>
                                                         sourceRoot != null &&
                                                         Util.IsAbsolute(sourceRoot) &&
                                                         Util.IsAbsolute(source)
                                                ? Util.Relative(sourceRoot, source)
                                                : source)
                      .ToArray();

            // Pass `true` below to allow duplicate names and sources. While source maps
            // are intended to be compressed and deduplicated, the TypeScript compiler
            // sometimes generates source maps with duplicates in them. See Github issue
            // #72 and bugzil.la/889492.
            _names   = new ArraySet(names, true);
            _sources = new ArraySet(sources, true);

            SourceRoot     = sourceRoot;
            SourcesContent = sourcesContent;
            _mappings      = mappings;
        }
 public static ISourceMapConsumer GetConsumer(SourceMapDescription sourceMapDescription)
 {
     return(sourceMapDescription.Sections != null
                         ? (ISourceMapConsumer) new IndexedSourceMapConsumer(sourceMapDescription)
                         : new BasicSourceMapConsumer(sourceMapDescription));
 }