ชุด Aurelia Webpack ไม่ทำงานกับ PhoneGap

ฉันกำลังมองหาวิธีที่จะทำให้แอป Aurelia/ESNext/Webpack ของฉันทำงานร่วมกับ PhoneGap (รุ่น Android) ไฟล์ที่รวมกลุ่มจะอยู่ในโฟลเดอร์ "www" เมื่อมองไปที่ผู้ตรวจสอบ ดูเหมือนว่าเซิร์ฟเวอร์ webpack กำลังร้องขอไฟล์บางไฟล์จากโฟลเดอร์ที่ไม่ถูกต้องเมื่อเรียกใช้แอปบนโทรศัพท์

มีความคิดวิธีกำหนดค่า Aurelia/Webpack อย่างถูกต้องเพื่อให้ทำงานได้อย่างไร

นี่คือ webpack.config.js ของฉัน:

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
    }))
  ]
});

นี่คือภาพหน้าจอจากผู้ตรวจสอบของฉัน: ภาพหน้าจอของตัวตรวจสอบ


person PVproject    schedule 30.04.2018    source แหล่งที่มา
comment
คุณช่วยแบ่งปันรายละเอียดเพิ่มเติมได้ไหม ตัวอย่างเช่น webpack.config.js เพื่อดูการกำหนดค่า? นอกจากนี้ จะเป็นประโยชน์หากคุณสามารถแบ่งปันข้อผิดพลาดที่คุณได้รับได้เช่นกัน   -  person Ray    schedule 30.04.2018
comment
ขอบคุณสำหรับการตอบกลับ โปรดดูโพสต์ที่อัปเดต   -  person PVproject    schedule 30.04.2018
comment
ขอบคุณ ดูเหมือนว่าจะเป็นไฟล์ webpack เริ่มต้น คุณช่วยบอกฉันเพิ่มเติมเกี่ยวกับโครงสร้างไดเร็กทอรีของคุณได้ไหม? โฟลเดอร์ dist อยู่ที่ไหน?, ไฟล์ index.html? และส่วน head จากไฟล์ html ของคุณเพื่อตรวจสอบว่าไฟล์เหล่านั้นมีการอ้างอิงหรือไม่   -  person Ray    schedule 30.04.2018
comment
ไฟล์ต้นฉบับ src อยู่ใน src   -  person PVproject    schedule 30.04.2018
comment
ตกลง. คุณแน่ใจได้ไหมว่า app.[chunk hash].bundle.js & vendor.[chunk hash].bundle.js มีการอ้างอิงอย่างถูกต้องจากตำแหน่งที่คุณโหลดไว้   -  person Ray    schedule 30.04.2018
comment
ฉันไม่แน่ใจว่าจะตอบอย่างไร... ฉันแค่รู้ว่า Webpack สร้างและจัดการพวกมัน การอ้างอิงเดียวคือ webpack-dev-server.js ภายใน index.ejs ถ้าฉันดูภายในโฟลเดอร์ www หลังจากสร้างบิลด์แล้ว ฉันจะเห็นไฟล์ที่คอมไพล์ทั้งหมด (รวมถึงไฟล์ที่คุณขอด้วย)   -  person PVproject    schedule 30.04.2018
comment
เมื่อดูที่ผู้ตรวจสอบฉันเห็นว่าดัชนี.html ถูกโหลดจาก file:///android_asset/www/ ในขณะที่อันอื่น ๆ ไม่ใช่   -  person PVproject    schedule 30.04.2018


คำตอบ (1)


มีโครงการตัวอย่างซึ่งสาธิตวิธีทำให้ Aurelia ทำงานบน Cordova (ซึ่งเป็นเอ็นจิ้นพื้นฐานสำหรับ PhoneGap): https://github.com/arjendeblok/aurelia-cordova-skeleton

ขั้นตอนสำคัญบางประการ (จาก readme):

ข้อกำหนดเบื้องต้น

  • โหนดJS
  • คอร์โดวา
  • เครื่องมือเฉพาะแพลตฟอร์มเช่น Android Studio

การติดตั้ง

หลังจาก npm install

  • cordova platform add browser สำหรับการเพิ่มแพลตฟอร์มเบราว์เซอร์ไปยัง Cordova
  • cordova platform add android สำหรับการเพิ่มแพลตฟอร์ม Android ลงใน Cordova

ทำงานในโหมดการพัฒนา

หลัง npm build (พร้อมตัวเลือกการพัฒนา):

  • cordova platform run browser สำหรับการเรียกใช้แอปในเบราว์เซอร์
  • cordova platform run android สำหรับการเรียกใช้แอปใน Android

ทำงานในโหมดการผลิต

หลัง npm build (พร้อมตัวเลือกการผลิต):

  • cordova platform build android

ไฟล์

ในกรณีที่พื้นที่เก็บข้อมูล GitHub หายไป

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="/thtel:*" />
    <allow-intent href="/thsms:*" />
    <allow-intent href="/thmailto:*" />
    <allow-intent href="/thgeo:*" />
    <platform name="android">
        <allow-intent href="/thmarket:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="/thitms:*" />
        <allow-intent href="/thitms-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