diff --git a/templates/html5/npm/package.json b/templates/html5/npm/package.json
new file mode 100755
index 000000000..2c7d7d992
--- /dev/null
+++ b/templates/html5/npm/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "::META_PACKAGE::",
+ "version": "::META_VERSION::",
+ "private": true,
+ "devDependencies": {
+ "haxe": "^5.0.10",
+ "haxe-loader": "^0.10.0",
+ "uglifyjs-webpack-plugin": "^1.3.0",
+ "webpack": "^4.20.2",
+ "webpack-cli": "^3.1.2",
+ "webpack-dev-server": "^3.1.9",
+ "webpack-merge": "^4.1.4"
+ },
+ "haxeDependencies": {
+ "haxe": "3.4.7"
+ },
+ "scripts": {
+ "build": "npm run build:prod",
+ "build:dev": "webpack --config webpack.dev.js",
+ "build:prod": "webpack --config webpack.prod.js",
+ "start": "npm run start:dev",
+ "start:dev": "webpack-dev-server --open --config webpack.dev.js",
+ "start:prod": "webpack-dev-server --open --config webpack.prod.js"
+ },
+ "dependencies": {}
+}
diff --git a/templates/html5/npm/webpack.common.js b/templates/html5/npm/webpack.common.js
new file mode 100755
index 000000000..04152203f
--- /dev/null
+++ b/templates/html5/npm/webpack.common.js
@@ -0,0 +1,20 @@
+const path = require ('path');
+
+module.exports = {
+ entry: "./../haxe/::if DEBUG::debug.hxml::else::::if FINAL::final.hxml::else::release.hxml::end::::end::",
+ output: {
+ path: path.resolve (__dirname, "dist"),
+ filename: "::OUTPUT_FILE::",
+ library: "lime",
+ libraryTarget: 'window',
+ libraryExport: 'lime'
+ },
+ module: {
+ rules: [
+ {
+ test: /\.hxml$/,
+ loader: 'haxe-loader',
+ }
+ ]
+ }
+};
\ No newline at end of file
diff --git a/templates/html5/npm/webpack.dev.js b/templates/html5/npm/webpack.dev.js
new file mode 100755
index 000000000..a770e2b88
--- /dev/null
+++ b/templates/html5/npm/webpack.dev.js
@@ -0,0 +1,10 @@
+const merge = require ('webpack-merge');
+const common = require ('./webpack.common.js');
+
+module.exports = merge (common, {
+ mode: 'development',
+ devServer: {
+ contentBase: './dist'
+ },
+ devtool: "inline-source-map",
+});
\ No newline at end of file
diff --git a/templates/html5/npm/webpack.prod.js b/templates/html5/npm/webpack.prod.js
new file mode 100755
index 000000000..317b40fb2
--- /dev/null
+++ b/templates/html5/npm/webpack.prod.js
@@ -0,0 +1,17 @@
+const webpack = require ('webpack');
+const merge = require ('webpack-merge');
+const UglifyJSPlugin = require ('uglifyjs-webpack-plugin');
+const common = require ('./webpack.common.js');
+
+module.exports = merge (common, {
+ mode: 'production',
+ devtool: "source-map",
+ plugins: [
+ new UglifyJSPlugin ({
+ sourceMap: true
+ }),
+ new webpack.DefinePlugin ({
+ 'process.env.NODE_ENV': JSON.stringify ('production')
+ })
+ ]
+});
\ No newline at end of file
diff --git a/tools/CommandLineTools.hx b/tools/CommandLineTools.hx
index 6668e0fbb..890f26b0c 100644
--- a/tools/CommandLineTools.hx
+++ b/tools/CommandLineTools.hx
@@ -980,6 +980,7 @@ class CommandLineTools
Log.println(" \x1b[3m(ios|tvos)\x1b[0m \x1b[1m-simulator\x1b[0m -- Target the device simulator");
Log.println(" \x1b[3m(ios)\x1b[0m \x1b[1m-simulator -ipad\x1b[0m -- Build/test for the iPad Simulator");
Log.println(" \x1b[3m(android)\x1b[0m \x1b[1m-emulator\x1b[0m -- Target the device emulator");
+ Log.println(" \x1b[3m(html5)\x1b[0m \x1b[1m-npm\x1b[0m -- Target HTML5 using an NPM project structure");
Log.println(" \x1b[3m(flash)\x1b[0m \x1b[1m-web\x1b[0m -- Test Flash target using a web template");
Log.println(" \x1b[3m(air)\x1b[0m \x1b[1m-ios\x1b[0m -- Target iOS instead of AIR desktop");
Log.println(" \x1b[3m(air)\x1b[0m \x1b[1m-android\x1b[0m -- Target Android instead of AIR desktop");
diff --git a/tools/platforms/HTML5Platform.hx b/tools/platforms/HTML5Platform.hx
index ed5c1825d..569e06161 100644
--- a/tools/platforms/HTML5Platform.hx
+++ b/tools/platforms/HTML5Platform.hx
@@ -25,6 +25,7 @@ import sys.FileSystem;
class HTML5Platform extends PlatformTarget
{
private var dependencyPath:String;
+ private var npm:Bool;
private var outputFile:String;
public function new(command:String, _project:HXProject, targetFlags:Map)
@@ -36,6 +37,18 @@ class HTML5Platform extends PlatformTarget
public override function build():Void
{
+ if (npm)
+ {
+ if (command == "build")
+ {
+ var buildCommand = "build:" + (project.targetFlags.exists("final") ? "prod" : "dev");
+ System.runCommand(targetDirectory + "/bin", "npm", ["run", buildCommand, "-s"]);
+ } else
+ {
+ return;
+ }
+ }
+
ModuleHelper.buildModules(project, targetDirectory + "/obj", targetDirectory + "/bin");
if (project.app.main != null)
@@ -76,9 +89,7 @@ class HTML5Platform extends PlatformTarget
for (dependency in project.dependencies)
{
- if (dependency.embed
- && StringTools.endsWith(dependency.path, ".js")
- && FileSystem.exists(dependency.path))
+ if (dependency.embed && StringTools.endsWith(dependency.path, ".js") && FileSystem.exists(dependency.path))
{
var script = File.getContent(dependency.path);
context.embeddedLibraries.push(script);
@@ -163,18 +174,27 @@ class HTML5Platform extends PlatformTarget
}
dependencyPath = project.config.getString("html5.dependency-path", "lib");
-
- if (targetFlags.exists("electron"))
- {
- dependencyPath = project.config.getString("html5.dependency-path", dependencyPath);
- }
-
outputFile = targetDirectory + "/bin/" + project.app.file + ".js";
+
+ try
+ {
+ if (targetFlags.exists("npm") || (FileSystem.exists(targetDirectory + "/bin/package.json") && !targetFlags.exists("electron")))
+ {
+ npm = true;
+ outputFile = project.app.file + ".js";
+ }
+ }
+ catch (e:Dynamic) {}
}
public override function run():Void
{
- if (targetFlags.exists("electron"))
+ if (npm)
+ {
+ var runCommand = "start:" + (project.targetFlags.exists("final") ? "prod" : "dev");
+ System.runCommand(targetDirectory + "/bin", "npm", ["run", runCommand, "-s"]);
+ }
+ else if (targetFlags.exists("electron"))
{
ElectronHelper.launch(project, targetDirectory + "/bin");
}
@@ -191,6 +211,7 @@ class HTML5Platform extends PlatformTarget
// project = project.clone ();
var destination = targetDirectory + "/bin/";
+ if (npm) destination += "dist/";
System.mkdir(destination);
var webfontDirectory = targetDirectory + "/obj/webfont";
@@ -272,6 +293,14 @@ class HTML5Platform extends PlatformTarget
}
}
+ if (npm)
+ {
+ for (i in 0...project.sources.length)
+ {
+ project.sources[i] = Path.tryFullPath(project.sources[i]);
+ }
+ }
+
// for (library in libraryNames.keys ()) {
//
// project.haxeflags.push ("-resource " + targetDirectory + "/obj/manifest/" + library + ".json@__ASSET_MANIFEST__" + library);
@@ -283,7 +312,7 @@ class HTML5Platform extends PlatformTarget
var context = project.templateContext;
context.WIN_FLASHBACKGROUND = project.window.background != null ? StringTools.hex(project.window.background, 6) : "";
- context.OUTPUT_DIR = targetDirectory;
+ context.OUTPUT_DIR = npm ? Path.tryFullPath(targetDirectory) : targetDirectory;
context.OUTPUT_FILE = outputFile;
if (project.targetFlags.exists("webgl"))
@@ -315,7 +344,7 @@ class HTML5Platform extends PlatformTarget
for (dependency in project.dependencies)
{
- if (!dependency.embed)
+ if (!dependency.embed || npm)
{
if (StringTools.endsWith(dependency.name, ".js"))
{
@@ -392,8 +421,8 @@ class HTML5Platform extends PlatformTarget
if (hasFormat[1]) urls.push("url('" + embeddedAsset.targetPath + ".eot?#iefix') format('embedded-opentype')");
if (hasFormat[3]) urls.push("url('" + embeddedAsset.targetPath + ".woff') format('woff')");
urls.push("url('" + embeddedAsset.targetPath + ext + "') format('truetype')");
- if (hasFormat[2]) urls.push("url('" + embeddedAsset.targetPath + ".svg#" + StringTools.urlEncode(embeddedAsset
- .fontName) + "') format('svg')");
+ if (hasFormat[2]) urls.push("url('" + embeddedAsset.targetPath + ".svg#" + StringTools.urlEncode(embeddedAsset.fontName)
+ + "') format('svg')");
var fontFace = "\t\t@font-face {\n";
fontFace += "\t\t\tfont-family: '" + embeddedAsset.fontName + "';\n";
@@ -422,6 +451,15 @@ class HTML5Platform extends PlatformTarget
ProjectHelper.recursiveSmartCopyTemplate(project, "html5/hxml", targetDirectory + "/haxe", context);
}
+ if (npm)
+ {
+ ProjectHelper.recursiveSmartCopyTemplate(project, "html5/npm", targetDirectory + "/bin", context);
+ if (!FileSystem.exists(targetDirectory + "/bin/node_modules"))
+ {
+ System.runCommand(targetDirectory + "/bin", "npm", ["install", "-s"]);
+ }
+ }
+
if (targetFlags.exists("electron"))
{
ProjectHelper.recursiveSmartCopyTemplate(project, "electron/template", destination, context);