2

我在 Windows 下安装 Akeneo PIM 时遇到问题;

我跟进了akeneo-install-instruction(pim-community-standard-v2.3)

https://docs.akeneo.com/2.3/install_pim/manual/installation_ce_archive.html

并对 package.json 进行必要的修改,就像这里所说的: Akeneo 安装 / NODE_PATH=node_modules notrecognized / yarn run webpack Error

然后我添加了 webpack,就像在这里完成的那样: https ://webkul.com/blog/yarn-run-webpack-issues-in-akeneo/ 和: npm install --save-dev webpack npm install

但我总是在开始后得到错误: yarn run webpack

./web/bundles/pimenrich/js/catalog-volume/header.ts 中的错误模块解析失败:C:\xampp\htdocs\pim-community-standard\web\bundles\pimenrich\js\catalog-volume\header。 ts Unexpected token (1:16) 您可能需要适当的加载程序来处理此文件类型。| 导入 BaseView = require('pimenrich/js/view/base'); | 从'下划线'导入*作为_;| @ ./web/js/module-registry.js 2:19263-19307 @ ./vendor/akeneo/pim-community-dev/webpack/require-context.js @ ./web/bundles/pimenrich/js/form/ builder.js @ ./web/bundles/pimenrich/js/index.js @ multi babel-polyfill ./web/bundles/pimenrich/js/index.js ...

我究竟做错了什么?

更新: webpack.config.js 文件的内容:

/* eslint-env es6 */
const fs = require('fs');
const process = require('process');
const rootDir = process.cwd();
const webpack = require('webpack');
const path = require('path');
const _ = require('lodash');

const WebpackCleanupPlugin = require('webpack-cleanup-plugin');
const LiveReloadPlugin = require('webpack-livereload-plugin');

const isProd = process.argv && process.argv.indexOf('--env=prod') > -1;
const sourcePath = path.join(rootDir, 'web/js/require-paths.js');

if (!fs.existsSync(sourcePath)) {
  throw new Error(`The web/js/require-paths.js module does not exist - You need to run
    "bin/console pim:install" or "bin/console pim:installer:dump-require-paths" before
    running webpack \n`);
}

const {getModulePaths, createModuleRegistry} = require('./webpack/requirejs-utils');
const {aliases, config} = getModulePaths(rootDir, __dirname, sourcePath);

createModuleRegistry(Object.keys(aliases), rootDir);

const babelPresets = [
  [
    'babel-preset-env',
    {
      targets: {
        browsers: ['firefox >= 45'],
      },
    },
  ],
];

if (isProd) {
  babelPresets.push('babel-preset-minify');
}

console.log('Starting webpack from', rootDir, 'in', isProd ? 'prod' : 'dev', 'mode');

module.exports = {
  stats: {
    hash: false,
    maxModules: 5,
    modules: false,
    timings: true,
    version: true,
  },
  target: 'web',
  entry: ['babel-polyfill', path.resolve(rootDir, './web/bundles/pimenrich/js/index.js')],
  output: {
    path: path.resolve('./web/dist/'),
    publicPath: '/dist/',
    filename: '[name].min.js',
    chunkFilename: '[name].bundle.js',
  },
  devtool: 'source-map',
  resolve: {
    symlinks: false,
    alias: _.mapKeys(aliases, (path, key) => `${key}$`),
    modules: [path.resolve('./web/bundles'), path.resolve('./node_modules')],
    extensions: ['.js', '.json', '.ts', '.tsx'],
  },
  module: {
    rules: [
      // Inject the module config (to replace module.config() from requirejs)
      {
        test: /\.js$/,
        exclude: /\/node_modules\/|\/spec\//,
        use: [
          {
            loader: path.resolve(__dirname, 'webpack/config-loader'),
            options: {
              configMap: config,
            },
          },
        ],
      },

      // Load html without needing to prefix the requires with 'text!'
      {
        test: /\.html$/,
        exclude: /node_modules|spec/,
        use: [
          {
            loader: 'raw-loader',
            options: {},
          },
        ],
      },

      // Expose the Backbone variable to window
      {
        test: /node_modules\/backbone\/backbone.js/,
        use: [
          {
            loader: 'expose-loader',
            options: 'Backbone',
          },
        ],
      },
      {
        test: /node_modules\/backbone\/backbone.js/,
        use: [
          {
            loader: 'imports-loader',
            options: 'this=>window',
          },
        ],
      },
      {
        test: /node_modules\/summernote\/dist\/summernote.js/,
        use: [
          {
            loader: 'imports-loader',
            options: 'require=>function(){}',
          },
          {
            loader: 'imports-loader',
            options: 'require.specified=>function(){}',
          },
        ],
      },
      // Expose jQuery to window
      {
        test: /node_modules\/jquery\/dist\/jquery.js/,
        use: [
          {
            loader: 'expose-loader',
            options: 'jQuery',
          },
          {
            loader: 'expose-loader',
            options: '$',
          },
        ],
      },

      // Expose the require-polyfill to window
      {
        test: path.resolve(__dirname, './webpack/require-polyfill.js'),
        use: [
          {
            loader: 'expose-loader',
            options: 'require',
          },
        ],
      },

      // Process the pim webpack files with babel
      {
        test: /\.js$/,
        include: /(web\/bundles|webpack|spec)/,
        exclude: /lib|node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: babelPresets,
            cacheDirectory: 'web/cache',
          },
        },
      },

      // Process the typescript loader files
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              configFile: path.resolve(__dirname, 'tsconfig.json'),
              context: path.resolve(rootDir),
            },
          },
          {
            loader: path.resolve(__dirname, 'webpack/config-loader'),
            options: {
              configMap: config,
            },
          },
        ],
        include: /(web\/bundles)/,
        exclude: /lib|node_modules|vendor|tests|src|packages/,
      },
    ],
  },

  watchOptions: {
    ignored: /node_modules|app|app\/cache|vendor/,
  },

  // Support old loader declarations
  resolveLoader: {
    moduleExtensions: ['-loader'],
  },

  plugins: [
    // Clean up the dist folder and source maps before rebuild
    new WebpackCleanupPlugin(),

    // Map modules to variables for global use
    new webpack.ProvidePlugin({_: 'underscore', Backbone: 'backbone', $: 'jquery', jQuery: 'jquery'}),

    // Ignore these directories when webpack watches for changes
    new webpack.WatchIgnorePlugin([
      path.resolve(rootDir, './node_modules'),
      path.resolve(rootDir, './app'),
      path.resolve(rootDir, './app/cache'),
      path.resolve(rootDir, './vendor'),
    ]),

    // Inject live reload to auto refresh the page (hmr not compatible with our app)
    new LiveReloadPlugin({appendScriptTag: true, ignore: /node_modules/}),

    // Split the app into chunks for performance
    new webpack.optimize.CommonsChunkPlugin({
      name: 'lib',
      minChunks: module => module.context && module.context.indexOf('lib') !== -1,
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: module => module.context && module.context.indexOf('node_modules') !== -1,
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': isProd ? JSON.stringify('production') : JSON.stringify('development'),
    }),
    new webpack.optimize.CommonsChunkPlugin({name: 'manifest'}),
  ],
};

更新: web\bundles\pimenrich\js\view\base.ts 文件的内容:

import * as _ from 'underscore';
import * as JQuery from 'jquery';
import * as Backbone from 'backbone';
import View from 'pimenrich/js/view/base-interface';
const mediator = require('oro/mediator');

/**
 * View base class
 *
 * @author    Julien Sanchez <julien@akeneo.com>
 * @author    Filips Alpe <filips@akeneo.com>
 * @copyright 2015 Akeneo SAS (http://www.akeneo.com)
 * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
class BaseView extends Backbone.View<any> implements View {
  private parent: View | null = null;
  private extensions: {[code: string]: View};

  readonly preUpdateEventName: string = 'pim_enrich:form:entity:pre_update';
  readonly postUpdateEventName: string = 'pim_enrich:form:entity:post_update';

  public code: string = 'form';
  public configured: boolean;
  public zones: {[code: string]: any};
  public targetZone: string;
  public position: number;

  /**
   * {@inheritdoc}
   */
  constructor(config: any) {
    super(config);

    this.extensions = {};
    this.zones = {};
    this.targetZone = '';
    this.configured = false;
  }

  /**
   * Configure the extension and its child extensions
   *
   * @return {JQueryPromise<any>}
   */
  configure(): JQueryPromise<any> {
    if (null === this.parent) {
      this.model = new Backbone.Model();
    }

    const extensionPromises = Object.values(this.extensions).map((extension: View) => {
      return extension.configure();
    });

    return JQuery.when(...extensionPromises).then(() => {
      this.configured = true;
    });
  }

  /**
   * Add a child extension to this extension
   *
   * @param {string} code      Extension's code
   * @param {View}   extension Backbone module of the extension
   * @param {string} zone      Targeted zone
   * @param {number} position  The position of the extension
   */
  addExtension(code: string, extension: View, zone: string, position: number) {
    extension.setParent(this);

    extension.code = code;
    extension.targetZone = zone;
    extension.position = position;

    if (undefined === this.extensions || null === this.extensions) {
      throw 'this.extensions have to be defined. Please ensure you called parent initialize() method.';
    }

    this.extensions[code] = extension;
  }

  /**
   * Get a child extension (the first extension matching the given code or ends with the given code)
   *
   * @param  {string} code
   * @return {View}
   */
  getExtension(code: string): View {
    const extensionKey = _.findKey(this.extensions, (extension: View) => {
      const expectedPosition = extension.code.length - code.length;

      return expectedPosition >= 0 && expectedPosition === extension.code.indexOf(code, expectedPosition);
    });

    return this.extensions[extensionKey];
  }

  /**
   * Set the parent of this extension
   *
   * @param {View} parent
   */
  setParent(parent: View) {
    this.parent = parent;
  }

  /**
   * Get the parent of the extension
   *
   * @return {View | null}
   */
  getParent(): View | null {
    return this.parent;
  }

  /**
   * Get the root extension
   *
   * @return {View}
   */
  getRoot(): View {
    let rootView = <View>this;
    let parent = this.getParent();

    while (null !== parent) {
      rootView = parent;
      parent = parent.getParent();
    }

    return rootView;
  }

  /**
   * Set data in the root model
   *
   * @param {any}                data
   * @param {{silent?: boolean}} options If silent is set to true, don't fire events
   *                                       pim_enrich:form:entity:pre_update and pim_enrich:form:entity:post_update
   */
  setData(data: any, options: {silent?: boolean} = {}) {
    if (!options.silent) {
      this.getRoot().trigger(this.preUpdateEventName, data);
    }

    this.getRoot().model.set(data, options);

    if (!options.silent) {
      this.getRoot().trigger(this.postUpdateEventName, data);
    }
  }

  /**
   * Get the form raw data (vanilla javascript object)
   *
   * @return {any}
   */
  getFormData(): any {
    return this.getRoot().model.toJSON();
  }

  /**
   * Get the form data (backbone model)
   *
   * @return {Backbone.Model}
   */
  getFormModel(): Backbone.Model {
    return this.getRoot().model;
  }

  /**
   * Called before removing the form from the view
   */
  shutdown() {
    this.doShutdown();

    Object.values(this.extensions).forEach((extension: View) => extension.shutdown());
  }

  /**
   * The actual shutdown method called on all extensions
   */
  doShutdown() {
    this.stopListening();
    this.undelegateEvents();
    this.$el.removeData().unbind();
    this.remove();

    Backbone.View.prototype.remove.call(this);
  }

  /**
   * {@inheritdoc}
   */
  render(): View {
    if (!this.configured) {
      return this;
    }

    return this.renderExtensions();
  }

  /**
   * Render the child extensions
   *
   * @return {View}
   */
  renderExtensions(): View {
    // If the view is no longer attached to the DOM, don't render the extensions
    if (undefined === this.el) {
      return this;
    }

    this.initializeDropZones();

    Object.values(this.extensions).forEach((extension: View) => {
      this.renderExtension(extension);
    });

    return this;
  }

  /**
   * Render a single extension
   *
   * @param {View} extension
   */
  renderExtension(extension: View) {
    var zone = this.getZone(extension.targetZone);

    if (null === zone) {
      throw new Error(
        `Can not render extension "${extension.code}" in "${this.code}": zone "${extension.targetZone}" does not exist`
      );
    }

    zone.appendChild(extension.el);

    extension.render();
  }

  /**
   * Initialize dropzone cache
   */
  initializeDropZones() {
    this.zones = this.$('[data-drop-zone]')
      .toArray()
      .reduce((zones: {[code: string]: HTMLElement}, zone: HTMLElement) => {
        return {...zones, [<string>zone.dataset.dropZone]: zone};
      }, {});

    this.zones['self'] = this.el;
  }

  /**
   * Get the drop zone for the given code
   *
   * @param {string} code
   *
   * @return {HTMLElement | null}
   */
  getZone(code: string): HTMLElement | null {
    if (!(code in this.zones)) {
      this.zones[code] = this.el.querySelector(`[data-drop-zone="${code}"]`);
    }

    if (!this.zones[code]) {
      return null;
    }

    return this.zones[code];
  }

  /**
   * Trigger event on each child extensions and their childs
   */
  triggerExtensions() {
    const options = Object.values(arguments);

    Object.values(this.extensions).forEach(extension => {
      extension.trigger.apply(extension, options);
      extension.triggerExtensions.apply(extension, options);
    });
  }

  /**
   * Listen on child extensions and their childs events
   *
   * @param {string}   code
   * @param {Function} callback
   */
  onExtensions(code: string, callback: any) {
    Object.values(this.extensions).forEach((extension: View) => {
      this.listenTo(extension, code, callback);
    });
  }

  /**
   * Get the root form code
   *
   * @return {string}
   */
  getFormCode(): string {
    return this.getRoot().code;
  }

  /**
   * Listen to given mediator events to trigger them locally (in the local root).
   * This way, extensions attached to this form don't have to listen "globally" on the mediator.
   *
   * @param {{[mediatorEvent: string]: string}} mediator events to forward:
   *                {'mediator:event:name': 'this:event:name', ...}
   */
  forwardMediatorEvents(events: {[mediatorEvent: string]: string}) {
    Object.keys(events).forEach((localEvent: string) => {
      const mediatorEvent = events[localEvent];

      this.listenTo(mediator, mediatorEvent, (...args: any[]) => {
        this.trigger(localEvent, ...args);
      });
    });
  }
}

export = BaseView;

4

0 回答 0