Пакет Aurelia Webpack не работает с PhoneGap

Я ищу способ заставить мое приложение Aurelia/ESNext/Webpack работать с PhoneGap (сборка Android). Связанные файлы помещаются в папку «www». Глядя на инспектор, кажется, что сервер веб-пакетов запрашивает некоторые файлы из неправильной папки при запуске приложения на телефоне.

Любая идея, как правильно настроить 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
Глядя на инспектора, я вижу, что index.html загружается из файла:///android_asset/www/, а остальные, по-видимому, нет.   -  person PVproject    schedule 30.04.2018


Ответы (1)


Существует пример проекта, который демонстрирует, как заставить Aurelia работать на Cordova (который является базовым движком для PhoneGap): https://github.com/arjendeblok/aurelia-cordova-skeleton

Несколько ключевых шагов (из ридми):

Предпосылки

  • NodeJS
  • Кордова
  • Любые инструменты для конкретной платформы, такие как 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="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <platform name="android">
        <allow-intent href="market:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-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