Bundel Aurelia Webpack tidak berfungsi dengan PhoneGap

Saya mencari cara agar Aplikasi Aurelia/ESNext/Webpack saya berfungsi dengan PhoneGap (Android build). File yang dibundel masuk ke folder "www". Melihat inspektur tampaknya server webpack meminta beberapa file dari folder yang salah saat menjalankan aplikasi di ponsel.

Adakah yang tahu cara mengkonfigurasi Aurelia/Webpack dengan benar agar berfungsi?

Ini webpack.config.js saya:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const project = require('./aurelia_project/aurelia.json');
const { AureliaPlugin, ModuleDependenciesPlugin } = require('aurelia-webpack-plugin');
const { optimize: { CommonsChunkPlugin, UglifyJsPlugin }, ProvidePlugin } = require('webpack');

// config helpers:
const ensureArray = (config) => config && (Array.isArray(config) ? config : [config]) || [];
const when = (condition, config, negativeConfig) =>
  condition ? ensureArray(config) : ensureArray(negativeConfig);

// primary config:
const title = 'App';
const outDir = path.resolve(__dirname, project.platform.output);
const srcDir = path.resolve(__dirname, 'src');
const nodeModulesDir = path.resolve(__dirname, 'node_modules');
const baseUrl = '/';

const cssRules = [
  { loader: 'css-loader' },
];

module.exports = ({production, server, extractCss, coverage, platform} = {}) => ({
  resolve: {
    extensions: ['.js'],
    modules: [srcDir, 'node_modules'],
  },
  entry: {
    app: ['aurelia-bootstrapper'],
    vendor: ['bluebird'],
  },
  output: {
    path: outDir,
    publicPath: (platform || 'browser') === 'mobile' ? './' : baseUrl,
    filename: production ? '[name].[chunkhash].bundle.js' : '[name].[hash].bundle.js',
    sourceMapFilename: production ? '[name].[chunkhash].bundle.map' : '[name].[hash].bundle.map',
    chunkFilename: production ? '[name].[chunkhash].chunk.js' : '[name].[hash].chunk.js'
  },
  devServer: {
    contentBase: outDir,
    // serve index.html for all 404 (required for push-state)
    historyApiFallback: true
  },
  devtool: production ? 'nosources-source-map' : 'cheap-module-eval-source-map',
  module: {
    rules: [
      // CSS required in JS/TS files should use the style-loader that auto-injects it into the website
      // only when the issuer is a .js/.ts file, so the loaders are not applied inside html templates
      {
        test: /\.css$/i,
        issuer: [{ not: [{ test: /\.html$/i }] }],
        use: extractCss ? ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: cssRules
        }) : ['style-loader', ...cssRules],
      },
      {
        test: /\.css$/i,
        issuer: [{ test: /\.html$/i }],
        // CSS required in templates cannot be extracted safely
        // because Aurelia would try to require it again in runtime
        use: cssRules
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        issuer: /\.[tj]s$/i
      },
      {
        test: /\.scss$/,
        use: ['css-loader', 'sass-loader'],
        issuer: /\.html?$/i
      },
      { test: /\.html$/i, loader: 'html-loader' },
      { test: /\.js$/i, loader: 'babel-loader', exclude: nodeModulesDir,
        options: coverage ? { sourceMap: 'inline', plugins: [ 'istanbul' ] } : {},
      },
      { test: /\.json$/i, loader: 'json-loader' },
      // use Bluebird as the global Promise implementation:
      { test: /[\/\\]node_modules[\/\\]bluebird[\/\\].+\.js$/, loader: 'expose-loader?Promise' },
      // embed small images and fonts as Data Urls and larger ones as files:
      { test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: { limit: 8192 } },
      { test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'url-loader', options: { limit: 10000, mimetype: 'application/font-woff2' } },
      { test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'url-loader', options: { limit: 10000, mimetype: 'application/font-woff' } },
      // load these fonts normally, as files:
      { test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader' },
    ]
  },
  plugins: [
    new AureliaPlugin(),
    new ProvidePlugin({
      'Promise': 'bluebird'
    }),
    new ModuleDependenciesPlugin({
      'aurelia-testing': [ './compile-spy', './view-spy' ]
    }),
    new HtmlWebpackPlugin({
      template: 'index.ejs',
      metadata: {
        // available in index.ejs //
        title, server, baseUrl
      }
    }),
    ...when(extractCss, new ExtractTextPlugin({
      filename: production ? '[contenthash].css' : '[id].css',
      allChunks: true
    })),
    ...when(production, new CommonsChunkPlugin({
      name: ['common']
    })),
    ...when(true, new CopyWebpackPlugin([
      { from: 'src/static', to: 'static', ignore: ['*.sass', '*.scss'] },
      { from: 'src/res', to: 'res' }
    ])),
    ...when(production, new UglifyJsPlugin({
      sourceMap: true
    }))
  ]
});

Berikut tangkapan layar dari inspektur saya: Tangkapan layar inspektur


person PVproject    schedule 30.04.2018    source sumber
comment
dapatkah Anda berbagi lebih detail. Misalnya webpack.config.js untuk melihat konfigurasinya? Juga akan sangat membantu jika Anda juga dapat membagikan kesalahan yang Anda terima.   -  person Ray    schedule 30.04.2018
comment
Terima kasih atas balasan Anda, silakan lihat postingan terupdate.   -  person PVproject    schedule 30.04.2018
comment
Terima kasih. Tampaknya ini adalah file webpack default. Bisakah Anda ceritakan lebih banyak tentang struktur direktori Anda? Di manakah lokasi folder dist?, file index.html? dan head bagian dari file html Anda untuk memeriksa apakah file tersebut direferensikan?   -  person Ray    schedule 30.04.2018
comment
File sumber src ada di src   -  person PVproject    schedule 30.04.2018
comment
Oke. Bisakah Anda memastikan app.[chunk hash].bundle.js & vendor.[chunk hash].bundle.js direferensikan dengan benar di mana pun Anda memuatnya?   -  person Ray    schedule 30.04.2018
comment
Saya tidak yakin bagaimana menjawabnya... Saya hanya tahu Webpack membuat dan mengelolanya. Satu-satunya referensi adalah webpack-dev-server.js di dalam index.ejs. Jika saya melihat ke dalam folder www setelah melakukan build, saya melihat semua file yang dikompilasi (termasuk yang Anda minta).   -  person PVproject    schedule 30.04.2018
comment
Saat melihat inspektur saya melihat bahwa index.html dimuat dari file:///Android_asset/www/ sedangkan yang lain tampaknya tidak.   -  person PVproject    schedule 30.04.2018


Jawaban (1)


Ada contoh proyek yang mendemonstrasikan cara membuat Aurelia berjalan di cordova (yang merupakan mesin dasar PhoneGap): https://github.com/arjendeblok/aurelia-cordova-skeleton

Beberapa langkah penting (dari readme):

Prasyarat

  • NodeJS
  • Kordoba
  • Peralatan khusus platform apa pun seperti Android Studio

Instalasi

Setelah npm install

  • cordova platform add browser untuk menambahkan platform browser ke cordova.
  • cordova platform add android untuk menambahkan platform android ke cordova.

Berjalan dalam mode pengembangan

Setelah npm build (dengan opsi pengembangan):

  • cordova platform run browser untuk menjalankan aplikasi di browser.
  • cordova platform run android untuk menjalankan aplikasi di Android.

Berjalan dalam mode produksi

Setelah npm build (dengan opsi produksi):

  • cordova platform build android

File

Jika repositori github hilang

config.xml

<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>HelloCordova</name>
    <description>
        A sample Apache Cordova application that responds to the deviceready event.
    </description>
    <author email="[email protected]" href="http://cordova.io">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <plugin name="cordova-plugin-whitelist" spec="1" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="/idtel:*" />
    <allow-intent href="/idsms:*" />
    <allow-intent href="/idmailto:*" />
    <allow-intent href="/idgeo:*" />
    <platform name="android">
        <allow-intent href="/idmarket:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="/iditms:*" />
        <allow-intent href="/iditms-apps:*" />
    </platform>
</widget>

webpack.config.js

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { AureliaPlugin } = require('aurelia-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin('app/app.css');
const bundleOutputDir = './www';

module.exports = ({ prod, cordova } = {}) => {
    const isDevBuild = !prod;
    const isCordova = cordova || false;

    return [{
        resolve: {
            extensions: ['.ts', '.js'],
            modules: ['src', 'node_modules'] // .map(x => path.resolve(x))
        },
        entry: {
            'app': 'aurelia-bootstrapper'
        },
        output: {
            path: path.resolve(bundleOutputDir),
            publicPath: '/',
            filename: 'app/app.js'
        },
        devServer: {
            contentBase: './www'
        },
        module: {
            rules: [
                { test: /\.ts$/i, include: /src/, use: 'ts-loader' },
                { test: /\.html$/i, use: 'html-loader' },
                {
                    test: /\.css$/i, use: isDevBuild ?
                        ['style-loader', 'css-loader'] :
                        extractCSS.extract('css-loader?minimize')
                },
                { test: /\.(png|jpg|jpeg|gif|svg|ttf|eot)$/, use: 'url-loader?limit=25000' },
                { test: /\.woff$/, use: "url-loader?prefix=font/&limit=5000&mimetype=application/font-woff" },
                { test: /\.woff2$/, use: "url-loader?prefix=font/&limit=5000&mimetype=application/font-woff2" }
            ]
        },

        plugins: [
            extractCSS,
            new webpack.DefinePlugin({ IS_DEV_BUILD: JSON.stringify(isDevBuild), IS_CORDOVA: JSON.stringify(isCordova) }),
            new AureliaPlugin({ aureliaApp: 'main', dist: 'native-modules' }),
            new HtmlWebpackPlugin({
                template: 'index.ejs',
                minify: prod ? {
                    removeComments: true,
                    collapseWhitespace: true
                } : undefined,
                metadata: {
                   prod, cordova
                }
            })
        ].concat(isDevBuild ? [
            new webpack.SourceMapDevToolPlugin({
                filename: '[file].map', // Remove this line if you prefer inline source maps
                moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]')  // Point sourcemap entries to the original file locations on disk
            })
        ] : [])
    }];
};

index.ejs

<!DOCTYPE html>
<html>
<head>
    <!--
        Customize this policy to fit your own app's needs. For more guidance, see:
            https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
        Some notes:
            * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
                * Enable inline JS: add 'unsafe-inline' to default-src
        -->
<% if (htmlWebpackPlugin.options.metadata.cordova) { %>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
<% } %>
    <meta name="format-detection" content="telephone=no">
    <meta name="msapplication-tap-highlight" content="no">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <title>Hello Cordova</title>
    <!-- imported CSS are concatenated and added automatically -->
</head>
<body aurelia-app="main">
    <p>booting...</p>
<% if (htmlWebpackPlugin.options.metadata.cordova) { %>
    <script type="text/javascript" src="cordova.js"></script>
<% } %>
    <!-- imported JS bundles are added automatically -->
</body>
</html>

src/cordova-events.ts

export class CordovaEvents {
    waitForDeviceReady(): Promise<boolean> {
        let p = new Promise<boolean>((resolve, reject) => {
            document.addEventListener('deviceready', () => {
                resolve(true);
            }, false);
        });
        return p;
    }
}

src/main.ts

import { Aurelia, PLATFORM } from 'aurelia-framework';
import { CordovaEvents } from './cordova-events';

declare const IS_DEV_BUILD: boolean; // The value is supplied by Webpack during the build
declare const IS_CORDOVA: boolean; // The value is supplied by Webpack during the build
export async function configure(aurelia: Aurelia) {
    if (IS_CORDOVA) {
        const cordova = new CordovaEvents();
        await cordova.waitForDeviceReady();
    }

    aurelia.use
        .standardConfiguration();

    if (IS_DEV_BUILD) {
        aurelia.use.developmentLogging();
    }

    await aurelia.start();

    await aurelia.setRoot(PLATFORM.moduleName('app'));
}

person Fred Kleuver    schedule 30.04.2018