From 9c01744c631cff3de5358b66302e6501f3701ecf Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Thu, 5 Jan 2017 16:22:34 +0200 Subject: [PATCH 1/2] feat: add plugin to support android/ios styleUrls When given an url "./style.css" and the resource "style.css" does not exist in the current directory, the plugin will change the url to "style.android|ios.css". The target platform should be provided in the options of the plugin. Closes #36 --- index.js | 2 + .../StyleUrlResolvePlugin.js | 99 +++++++++++++++++++ webpack.common.js.angular.template | 1 + 3 files changed, 102 insertions(+) create mode 100644 resource-resolver-plugins/StyleUrlResolvePlugin.js diff --git a/index.js b/index.js index 26dd7d67..84207b62 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,8 @@ var sources = require("webpack-sources"); var fs = require("fs"); var path = require("path"); +exports.StyleUrlResolvePlugin = require('./resource-resolver-plugins/StyleUrlResolvePlugin'); + //HACK: changes the JSONP chunk eval function to `global["nativescriptJsonp"]` // applied to tns-java-classes.js only exports.NativeScriptJsonpPlugin = function(options) { diff --git a/resource-resolver-plugins/StyleUrlResolvePlugin.js b/resource-resolver-plugins/StyleUrlResolvePlugin.js new file mode 100644 index 00000000..4ba73d8e --- /dev/null +++ b/resource-resolver-plugins/StyleUrlResolvePlugin.js @@ -0,0 +1,99 @@ +const ts = require("typescript"); +const fs = require("fs"); +const path = require("path"); + +const StyleUrlResolvePlugin = (function() { + function StyleUrlResolvePlugin(options) { + if (!options || !options.platform) { + throw new Error(`Target platform must be specified!`); + } + + this.platform = options.platform; + } + + StyleUrlResolvePlugin.prototype.apply = function (compiler) { + compiler.plugin("make", (compilation, callback) => { + const aotPlugin = getAotPlugin(compilation); + aotPlugin._program.getSourceFiles() + .map(sf => this.setCurrentDirectory(sf)) + .forEach(sf => this.usePlatformStyleUrl(sf)); + + callback(); + }) + }; + + function getAotPlugin(compilation) { + let maybeAotPlugin = compilation._ngToolsWebpackPluginInstance; + if (!maybeAotPlugin) { + throw new Error(`This plugin must be used with the AotPlugin!`); + } + + return maybeAotPlugin; + } + + StyleUrlResolvePlugin.prototype.usePlatformStyleUrl = function(sourceFile) { + this.setCurrentDirectory(sourceFile); + ts.forEachChild(sourceFile, node => this.traverseDecorators(node)); + } + + StyleUrlResolvePlugin.prototype.setCurrentDirectory = function(sourceFile) { + this.currentDirectory = path.resolve(sourceFile.path, ".."); + + return sourceFile; + } + + StyleUrlResolvePlugin.prototype.traverseDecorators = function(node) { + if (node.kind !== ts.SyntaxKind.ClassDeclaration || !node.decorators) { + return; + } + + node.decorators.forEach(decorator => { + this.traverseDecoratorArguments(decorator.expression.arguments); + }); + } + + StyleUrlResolvePlugin.prototype.traverseDecoratorArguments = function(args) { + args.forEach(arg => this.traverseProperties(arg.properties)); + } + + StyleUrlResolvePlugin.prototype.traverseProperties = function(properties) { + properties.filter(isStyleUrls) + .forEach(prop => this.traversePropertyElements(prop)); + } + + function isStyleUrls(property) { + return property.name.text === "styleUrls"; + } + + StyleUrlResolvePlugin.prototype.traversePropertyElements = function(property) { + property.initializer.elements + .filter(el => this.notPlatformUrl(el.text)) + .filter(el => this.noCommonFile(el.text)) + .forEach(el => this.replaceStyleUrlsValue(el)); + } + + StyleUrlResolvePlugin.prototype.notPlatformUrl = function(styleUrl) { + let extensionStartIndex = styleUrl.lastIndexOf("."); + let extension = styleUrl.slice(extensionStartIndex); + + return !styleUrl.endsWith(`.${this.platform}${extension}`); + } + + StyleUrlResolvePlugin.prototype.noCommonFile = function(styleUrl) { + let stylePath = path.resolve(this.currentDirectory, styleUrl); + + return !fs.existsSync(stylePath); + } + + StyleUrlResolvePlugin.prototype.replaceStyleUrlsValue = function(element) { + const extensionStartIndex = element.text.lastIndexOf("."); + const prefix = element.text.slice(0, extensionStartIndex); + const currentExtension = element.text.slice(extensionStartIndex); + + element.text = `${prefix}.${this.platform}${currentExtension}`; + } + + return StyleUrlResolvePlugin; +})(); + +module.exports = StyleUrlResolvePlugin; diff --git a/webpack.common.js.angular.template b/webpack.common.js.angular.template index 9ac61f7e..43edb69b 100644 --- a/webpack.common.js.angular.template +++ b/webpack.common.js.angular.template @@ -51,6 +51,7 @@ module.exports = function (platform, destinationApp) { entryModule: path.resolve(__dirname, "app/app.module#AppModule"), typeChecking: false }), + new nsWebpack.StyleUrlResolvePlugin({platform}), ]; if (process.env.npm_config_uglify) { From e1b22a16038c52f04fb879e95665cebf4826b0db Mon Sep 17 00:00:00 2001 From: sis0k0 Date: Thu, 5 Jan 2017 17:09:06 +0200 Subject: [PATCH 2/2] review fixes --- resource-resolver-plugins/StyleUrlResolvePlugin.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/resource-resolver-plugins/StyleUrlResolvePlugin.js b/resource-resolver-plugins/StyleUrlResolvePlugin.js index 4ba73d8e..fefbf109 100644 --- a/resource-resolver-plugins/StyleUrlResolvePlugin.js +++ b/resource-resolver-plugins/StyleUrlResolvePlugin.js @@ -15,7 +15,6 @@ const StyleUrlResolvePlugin = (function() { compiler.plugin("make", (compilation, callback) => { const aotPlugin = getAotPlugin(compilation); aotPlugin._program.getSourceFiles() - .map(sf => this.setCurrentDirectory(sf)) .forEach(sf => this.usePlatformStyleUrl(sf)); callback(); @@ -38,8 +37,6 @@ const StyleUrlResolvePlugin = (function() { StyleUrlResolvePlugin.prototype.setCurrentDirectory = function(sourceFile) { this.currentDirectory = path.resolve(sourceFile.path, ".."); - - return sourceFile; } StyleUrlResolvePlugin.prototype.traverseDecorators = function(node) { @@ -68,7 +65,7 @@ const StyleUrlResolvePlugin = (function() { StyleUrlResolvePlugin.prototype.traversePropertyElements = function(property) { property.initializer.elements .filter(el => this.notPlatformUrl(el.text)) - .filter(el => this.noCommonFile(el.text)) + .filter(el => this.noMultiplatformFile(el.text)) .forEach(el => this.replaceStyleUrlsValue(el)); } @@ -79,7 +76,7 @@ const StyleUrlResolvePlugin = (function() { return !styleUrl.endsWith(`.${this.platform}${extension}`); } - StyleUrlResolvePlugin.prototype.noCommonFile = function(styleUrl) { + StyleUrlResolvePlugin.prototype.noMultiplatformFile = function(styleUrl) { let stylePath = path.resolve(this.currentDirectory, styleUrl); return !fs.existsSync(stylePath);