"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
// TODO: fix typings.
// tslint:disable-next-line:no-global-tslint-disable
// tslint:disable:no-any
const path = require("path");
const angular_compiler_plugin_1 = require("./angular_compiler_plugin");
const benchmark_1 = require("./benchmark");
const sourceMappingUrlRe = /^\/\/# sourceMappingURL=[^\r\n]*/gm;
function ngcLoader() {
    const cb = this.async();
    const sourceFileName = this.resourcePath;
    const timeLabel = `ngcLoader+${sourceFileName}+`;
    if (!cb) {
        throw new Error('This loader needs to support asynchronous webpack compilations.');
    }
    benchmark_1.time(timeLabel);
    const plugin = this._compilation._ngToolsWebpackPluginInstance;
    if (!plugin) {
        throw new Error('The AngularCompilerPlugin was not found. '
            + 'The @ngtools/webpack loader requires the plugin.');
    }
    // We must verify that the plugin is an instance of the right class.
    // Throw an error if it isn't, that often means multiple @ngtools/webpack installs.
    if (!(plugin instanceof angular_compiler_plugin_1.AngularCompilerPlugin) || !plugin.done) {
        throw new Error('Angular Compiler was detected but it was an instance of the wrong class.\n'
            + 'This likely means you have several @ngtools/webpack packages installed. '
            + 'You can check this with `npm ls @ngtools/webpack`, and then remove the extra copies.');
    }
    benchmark_1.time(timeLabel + '.ngcLoader.AngularCompilerPlugin');
    plugin.done
        .then(() => {
        benchmark_1.timeEnd(timeLabel + '.ngcLoader.AngularCompilerPlugin');
        const result = plugin.getCompiledFile(sourceFileName);
        if (result.sourceMap) {
            // Process sourcemaps for Webpack.
            // Remove the sourceMappingURL.
            result.outputText = result.outputText.replace(sourceMappingUrlRe, '');
            // Set the map source to use the full path of the file.
            const sourceMap = JSON.parse(result.sourceMap);
            sourceMap.sources = sourceMap.sources.map((fileName) => {
                return path.join(path.dirname(sourceFileName), fileName);
            });
            result.sourceMap = sourceMap;
        }
        // Manually add the dependencies for TS files.
        // Type only imports will be stripped out by compilation so we need to add them as
        // as dependencies.
        // Component resources files (html and css templates) also need to be added manually for
        // AOT, so that this file is reloaded when they change.
        if (sourceFileName.endsWith('.ts')) {
            result.errorDependencies.forEach(dep => this.addDependency(dep));
            const dependencies = plugin.getDependencies(sourceFileName);
            dependencies
                .filter(d => d.endsWith('index.ts'))
                .forEach(d => dependencies.push(...plugin.getDependencies(d)));
            [...new Set(dependencies)].forEach(dep => {
                plugin.updateChangedFileExtensions(path.extname(dep));
                this.addDependency(dep);
            });
        }
        // NgFactory files depend on the component template, but we can't know what that file
        // is (if any). So we add all the dependencies that the original component file has
        // to the factory as well, which includes html and css templates, and the component
        // itself (for inline html/templates templates).
        const ngFactoryRe = /\.ngfactory.js$/;
        if (ngFactoryRe.test(sourceFileName)) {
            const originalFile = sourceFileName.replace(ngFactoryRe, '.ts');
            this.addDependency(originalFile);
            const origDependencies = plugin.getDependencies(originalFile);
            origDependencies.forEach(dep => this.addDependency(dep));
        }
        // NgStyle files depend on the style file they represent.
        // E.g. `some-style.less.shim.ngstyle.js` depends on `some-style.less`.
        // Those files can in turn depend on others, so we have to add them all.
        const ngStyleRe = /(?:\.shim)?\.ngstyle\.js$/;
        if (ngStyleRe.test(sourceFileName)) {
            const styleFile = sourceFileName.replace(ngStyleRe, '');
            for (const dep of plugin.getResourceDependencies(styleFile)) {
                this.addDependency(dep);
            }
        }
        // Add type-only dependencies that should trigger a rebuild when they change.
        const typeDependencies = plugin.getTypeDependencies(sourceFileName);
        typeDependencies.forEach(dep => this.addDependency(dep));
        benchmark_1.timeEnd(timeLabel);
        cb(null, result.outputText, result.sourceMap);
    })
        .catch(err => {
        benchmark_1.timeEnd(timeLabel);
        cb(err);
    });
}
exports.ngcLoader = ngcLoader;