def cascade(args):
    mapunder = create_from_json(args.mapunder.read())
    mapover = create_from_json(args.mapover.read())

    mapunder.sourceRoot = absolute_sourceRoot(args.mapunder.name, mapunder.sourceRoot)
    mapover.sourceRoot = absolute_sourceRoot(args.mapover.name, mapover.sourceRoot)

    resultmap = cascade_sourcemaps(mapunder, mapover)
    resultmap.sourceRoot, resultmap.sources = root_paths([
        relpath( source, start=abspath(dirname(args.outmap.name)) ) \
        for source in resultmap.sources
    ])
    args.outmap.write(resultmap.dump())

    if args.fixmapurl is not None:
        rcode_lines = args.fixmapurl.readlines()
        try:
            _, markerline = discover_sourcemap(rcode_lines, return_line_number=True)
            rcode_lines.pop(markerline)
            args.fixmapurl.seek(0)  # rewrite file to remove old url
            args.fixmapurl.truncate()
            args.fixmapurl.writelines(rcode_lines)
        except IndexError:
            pass
        args.fixmapurl.write('//# sourceMappingURL={}'.format(
            relpath( args.outmap.name, start=dirname(args.fixmapurl.name) )
        ))
def concat(args):
    result_code = []
    smaplist = []
    for fconfig in args.file:
        code_lines = fconfig['file'].readlines()
        try:
            mapurl, markerline = discover_sourcemap(code_lines, return_line_number=True)
            code_lines.pop(markerline) # remove sourcemap url marker from code
        except IndexError:
            mapurl, markerline = None, None # sourcemap not detected

        mappath = None
        if 'map' in fconfig:
            mappath = fconfig['map']  # direct setting sourcemap path
        else:
            if mapurl is not None:
                mappath = filepath_relative_to_file(fconfig['file'].name, mapurl)

        if mappath is not None:
            # sourcemap file exists
            with open(mappath, 'r') as f:
                smap = create_from_json(f.read())
            if markerline is not None:
                try:
                    smap.lines.pop(markerline)  # deleting same line from sourcemap too
                except IndexError:
                    pass
            if len(smap.lines) > len(code_lines):
                raise ValueError('Sourcemap for file {} contains more lines than original code'.format(fconfig['file'].name))
            while len(smap.lines) < len(code_lines):
                smap.lines.append([])

            smap.sourceRoot = absolute_sourceRoot(mappath, smap.sourceRoot)

        elif fconfig.get('lexer', None) is not None:
            smap = ( abspath(fconfig['file'].name), lex(code_lines, fconfig['lexer']) )
        else:
            smap = len(code_lines)
        result_code.extend(code_lines)
        smaplist.append(smap)
    mergedmap = concat_sourcemaps(*smaplist)

    mergedmap.sourceRoot, mergedmap.sources = root_paths([
        relpath( source, start=abspath(dirname(args.outmap.name)) ) \
        for source in mergedmap.sources
    ])

    args.outmap.write(mergedmap.dump())
    args.outfile.writelines(result_code)
    args.outfile.write('//# sourceMappingURL={}'.format(
        relpath( args.outmap.name, start=dirname(args.outfile.name) )
    ))
def lookup(args):
    if args.mapfile:
        mapname = args.mapfile
    else:
        mapname = filepath_relative_to_file(args.file.name, discover_sourcemap(args.file))
    with open(mapname, 'r') as mapfile:
        mp = create_from_json(mapfile.read())
    try:
        lk = mp.lookup(args.line, args.column)
    except IndexError:
        exit(1)  # if position not found
    sourcename = filepath_relative_to_file(mapname, lk['source'])
    if not args.showcode:
        print('{} {} {}'.format(sourcename, lk['line'], lk['column']))
    else:
        print_near(sourcename, lk['line'], lk['column'])