Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: Transformation map 0 must have exactly one source file #85

Closed
milahu opened this issue Sep 23, 2020 · 2 comments
Closed

Error: Transformation map 0 must have exactly one source file #85

milahu opened this issue Sep 23, 2020 · 2 comments

Comments

@milahu
Copy link
Contributor

milahu commented Sep 23, 2020

please explain this error

Error: Transformation map 0 must have exactly one source file.
Did you specify these with the most recent transformation maps first?

this is required for all but the last/oldest sourcemap (index -1)

i mean, why must maps 0 to -2 have only one source file?
this looks more like 'not implemented' than 'error'

put differently:
why does remapper not support convergent transform chains?
= multiple input files + single output file, arranged in a tree structure

also, if i recall right, only the first entry of maps[-1].sources
is propagated to result.sources. doesnt that break sourcesIdx values?

reproduce
const remapper = require('@ampproject/remapping');

// segment =
//     [ columnIdx_gen ]
//   | [ columnIdx_gen, sourcesIdx, lineIdx, columnIdx ]
//   | [ columnIdx_gen, sourcesIdx, lineIdx, columnIdx, namesIdx ]

const trigger_error = 1;

const sourcemapList = [
  // map 0
  {
    version: 3,
    sources: trigger_error
      ? [
          'file-3', // src 0
          'file-4', // src 1
        ]
      : [
          'file-3',
        ],
    names: [],
    mappings: [
      trigger_error
      ? [
          [0, 0, 0, 0], // col 0 = src 0
          [1, 1, 0, 0], // col 1 = src 1
        ]
      : [
          [0, 0, 0, 0],
        ]
    ]
  },
  // map 1
  {
    version: 3,
    sources: [
      'file-1', // src 0
      'file-2', // src 1
    ],
    names: [],
    mappings: [
      [
        [0, 0, 0, 0], // col 0 = src 0
        [1, 1, 0, 0], // col 1 = src 1
      ]
    ]
  }
];

const sourcemap = remapper(
  sourcemapList,
  function loader(file) {
    return null;
  }
);
console.dir(sourcemap);

actual result with trigger_error = 0:

    sources: [
      'file-1', // src 0 of map 1
    ],

expected result:

    sources: [
      'file-1',
      'file-2',
      'file-3',
      'file-4',
    ],
@jridgewell
Copy link
Collaborator

Ok, so there are a few parts here.

i mean, why must maps 0 to -2 have only one source file?

This is only a requirement when using the array form. The array form is explicitly for multiple transforms of a single file. Eg, think about running a minifier on a file, or a babel transform. These don't combine multiple source files into one, only a single file into a single file.

The array form provides this as a convenience. You can think of it a bit like minify(transform(file)) mapping to [minifiedMap, transformedMap, fileMap]. Here, minifiedMap has no other inputs, and the same with transformedMap. And because the next item in the array is a single map, we can assume that minifiedMap's input is transformedMap, and transformedMap's input is fileMap.

You'll need to use multiple calls to remapping to support multiple input files with multiple input files.

also, if i recall right, only the first entry of maps[-1].sources is propagated to result.sources. doesn't that break sourcesIdx values?

I think this is because your first sourcemap (in the trigger_error = 0 case) only has a single sourcemap segment. Since you only reference line 0 of of the next sourcemap, you only take the mapping from file-1. If you were to include a second segment for [1, 0, 0, 1], you'd see both file-1 and file-2.

@milahu
Copy link
Contributor Author

milahu commented Sep 29, 2020

You'll need to use multiple calls to remapping to support multiple input files with multiple input files.

but then i still have the same limitation:
all except the last map must have only one source file

now solved this with the loader interface

const remapping = require('@ampproject/remapping');
const MagicString = require('magic-string');
const SourcemapCodec = require('sourcemap-codec');

function _dir(obj) {
  console.log(require('util').inspect(obj, false, null, true));
}

function push(bundle, filename, source) {
  bundle.addSource({
    filename,
    content: new MagicString(source),
    separator: '\n',
    //separator: '', // ERROR. probably a bug in magic-string
  });
  // mutate global
  code[filename] = source;
}

function done(bundle, filename, extraOptions = {}) {
  // mutate globals
  code[filename] = bundle.toString();
  map[filename] = bundle.generateDecodedMap({
    file: filename,
    includeContent: true,
    hires: false,
    ...extraOptions,
  });
}

const code = {};
const map = {};
let b, s;

b = new MagicString.Bundle();
push(b, 'src-8', ' interface');
push(b, 'src-7', ' loader');
push(b, 'src-6', ' the');
done(b, 'bundle-3');

b = new MagicString.Bundle();
push(b, 'bundle-3', code['bundle-3']);
push(b, 'src-5', ' with');
push(b, 'src-4', ' works');
push(b, 'src-3', ' this');
done(b, 'bundle-2');

b = new MagicString.Bundle();
push(b, 'bundle-2', code['bundle-2']);
push(b, 'src-2', ' world');
push(b, 'src-1', ' hello');
done(b, 'bundle-1');

//console.log('code:');
//_dir(code);

//console.log('sources:');
//_dir(Object.keys(map).map(f => [f, map[f].sources]));

/*
        map-1       map-2       map-3
          ^           ^           ^
bundle-1 <  bundle-2 <  bundle-3 <
            src-1       src-3       src-6
            src-2       src-4       src-7
                        src-5       src-8
*/

// use remapping with loader interface
const map_combined_1 = remapping(
  map['bundle-1'],
  function loader(filename) {
    if (map[filename])
      console.log('loading map for '+filename);
    else
      console.log('found original source '+filename);
    return map[filename] || null;
  }
);
map_combined_1.mappings = SourcemapCodec.decode(map_combined_1.mappings);

console.log('combined sources:');
_dir(map_combined_1.sources);

console.log('combined mappings:');
_dir(map_combined_1.mappings);

// loop mappings
console.log('code = '+JSON.stringify(code['bundle-1']));
const line_list = code['bundle-1'].split('\n');
map_combined_1.mappings.forEach((segment_list, output_line) => {
  segment_list.forEach(([output_column, source, line, column, name], segment) => {
    const last_output_column = segment_list[segment + 1]
      ? segment_list[segment + 1][0]
      : undefined;
    if (line_list[output_line])
      console.log(
        'token '+JSON.stringify(line_list[output_line].slice(output_column, last_output_column))
        +' <- '+map_combined_1.sources[source]
        //+' @ line '+line+' column '+column
        +' = '+JSON.stringify(code[map_combined_1.sources[source]])
      );
    else
      console.log('error: output_line '+output_line+' not in code');
  })
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants