<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import cloneDeep from 'lodash.clonedeep';

/**
 * export defaults one object that is an example profile, in the Gecko format,
 * i.e. the format that nsIProfiler.getProfileDataAsync outputs.
 */

const parentProcessBinary = {
  breakpadId: 'F1D957D30B413D55A539BBA06F90DD8F0',
  debugName: 'firefox',
  name: 'firefox',
  path: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox',
  debugPath: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox',
  start: 0x100000000,
  end: 0x100000000 + 10000,
  arch: 'x86_64',
};

const contentProcessBinary = {
  breakpadId: '9F950E2CE3CD3E1ABD06D80788B606E60',
  debugName: 'firefox-webcontent',
  name: 'firefox-webcontent',
  path:
    '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-webcontent.app/Contents/MacOS/firefox-webcontent',
  debugPath:
    '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-webcontent.app/Contents/MacOS/firefox-webcontent',
  start: 0x100000000,
  end: 0x100000000 + 10000,
  arch: 'x86_64',
};

const extraBinaries = [
  {
    breakpadId: '1000000000000000000000000000000A1',
    debugName: 'examplebinary',
    name: 'examplebinary',
    path: '/tmp/examplebinary',
    debugPath: '/tmp/examplebinary',
    start: 0x200000000,
    end: 0x200000000 + 20,
    arch: 'x86_64',
  },
  {
    breakpadId: '100000000000000000000000000000A27',
    debugName: 'examplebinary2.pdb',
    name: 'examplebinary2',
    path: 'C:\\examplebinary2',
    debugPath: 'C:\\examplebinary2.pdb',
    start: 0x200000000 + 20,
    end: 0x200000000 + 40,
    arch: 'x86_64',
  },
];

const thread = {
  samples: {
    schema: {
      stack: 0,
      time: 1,
      responsiveness: 2,
      rss: 3,
      uss: 4,
      frameNumber: 5,
      power: 6,
    },
    data: [
      [1, 0, 0], // (root), 0x100000f84
      [2, 1, 0], // (root), 0x100000f84, 0x100001a45
      [2, 2, 0], // (root), 0x100000f84, 0x100001a45
      [3, 3, 0], // (root), 0x100000f84, Startup::XRE_Main
      [0, 4, 0], // (root)
      [1, 5, 0], // (root), 0x100000f84
      [4, 6, 0], // (root), 0x100000f84, frobnicate
    ],
  },
  stackTable: {
    schema: { prefix: 0, frame: 1 },
    data: [
      [null, 0], // (root)
      [0, 1], // (root), 0x100000f84
      [1, 2], // (root), 0x100000f84, 0x100001a45
      [1, 3], // (root), 0x100000f84, Startup::XRE_Main
      [1, 4], // (root), 0x100000f84, frobnicate
    ],
  },
  frameTable: {
    schema: {
      location: 0,
      implementation: 1,
      optimizations: 2,
      line: 3,
      category: 4,
    },
    data: [
      [0], // (root)
      [1], // 0x100000f84
      [2], // 0x100001a45
      [3, null, null, 4391, 16], // Startup::XRE_Main, line 4391, category 16
      [7, 6, null, 34], // frobnicate, implementation 'baseline', line 34
    ],
  },
  markers: {
    schema: { name: 0, time: 1, data: 2 },
    data: [
      // Please keep the next marker at the start if you add more markers in
      // this structure.
      // We want to test tracing markers with the missing "start" marker. So
      // here is only the "end" marker without a matching "start" marker.
      [
        10, // Rasterize
        1,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      // This marker is filtered out
      [4, 2, { category: 'VsyncTimestamp', vsync: 0 }],
      [
        5, // Reflow
        3,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        10, // Rasterize
        4,
        {
          category: 'Paint',
          interval: 'start',
          type: 'tracing',
        },
      ],
      [
        10, // Rasterize
        5,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        8,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        9,
        11, // Note: this marker is out of order on purpose, to test we correctly sort
        {
          // MinorGC at time 11ms from 11ms to 12ms
          startTime: 11,
          endTime: 12,
        },
      ],
      [
        8,
        9,
        {
          // DOMEvent at time 9ms from 9ms to 10ms
          startTime: 9,
          endTime: 10,
          timeStamp: 1,
          type: 'mouseout',
          phase: 3,
        },
      ],
      [
        11, // UserTiming
        12,
        {
          startTime: 12,
          endTime: 13,
          type: 'UserTiming',
          name: 'processing-thread',
          entryType: 'measure',
        },
      ],
      // Nested reflows
      [
        5, // Reflow
        13,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        14,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        15,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        18,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      // Starting a tracing but never finishing it.
      // Please keep it at the end if you add more markers in this structure
      [
        10, // Rasterize
        20,
        {
          category: 'Paint',
          interval: 'start',
          type: 'tracing',
        },
      ],
    ],
  },
  stringTable: [
    '(root)',
    '0x100000f84',
    '0x100001a45',
    'Startup::XRE_Main',
    'VsyncTimestamp',
    'Reflow',
    'baseline',
    'frobnicate (chrome://blargh:34)',
    'DOMEvent',
    'MinorGC',
    'Rasterize',
    'UserTiming',
  ],
};

const parentProcessMeta = {
  abi: 'x86_64-gcc3',
  interval: 1,
  misc: 'rv:48.0',
  oscpu: 'Intel Mac OS X 10.11',
  platform: 'Macintosh',
  processType: 0,
  product: 'Firefox',
  stackwalk: 1,
  startTime: 1460221352723.438,
  toolkit: 'cocoa',
  version: 5,
};

const contentProcessMeta = Object.assign({}, parentProcessMeta, {
  processType: 2,
  startTime: parentProcessMeta.startTime + 1000, // content process was launched 1 second after parent process
});

const contentProcessProfile = {
  meta: contentProcessMeta,
  libs: [contentProcessBinary].concat(extraBinaries), // libs are stringified in the Gecko profile
  threads: [Object.assign({ name: 'GeckoMain', processType: 'tab' }, thread)],
  processes: [],
};

const profile = {
  meta: parentProcessMeta,
  libs: [parentProcessBinary].concat(extraBinaries),
  threads: [
    Object.assign({ name: 'GeckoMain', processType: 'default' }, thread),
    Object.assign({ name: 'Compositor', processType: 'default' }, thread),
  ],
  processes: [contentProcessProfile],
};

export default function createProfile() {
  return cloneDeep(profile);
}
<commit_msg>Deep clone the thread so updating a thread won't affect the other one<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import cloneDeep from 'lodash.clonedeep';

/**
 * export defaults one object that is an example profile, in the Gecko format,
 * i.e. the format that nsIProfiler.getProfileDataAsync outputs.
 */

const parentProcessBinary = {
  breakpadId: 'F1D957D30B413D55A539BBA06F90DD8F0',
  debugName: 'firefox',
  name: 'firefox',
  path: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox',
  debugPath: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox',
  start: 0x100000000,
  end: 0x100000000 + 10000,
  arch: 'x86_64',
};

const contentProcessBinary = {
  breakpadId: '9F950E2CE3CD3E1ABD06D80788B606E60',
  debugName: 'firefox-webcontent',
  name: 'firefox-webcontent',
  path:
    '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-webcontent.app/Contents/MacOS/firefox-webcontent',
  debugPath:
    '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-webcontent.app/Contents/MacOS/firefox-webcontent',
  start: 0x100000000,
  end: 0x100000000 + 10000,
  arch: 'x86_64',
};

const extraBinaries = [
  {
    breakpadId: '1000000000000000000000000000000A1',
    debugName: 'examplebinary',
    name: 'examplebinary',
    path: '/tmp/examplebinary',
    debugPath: '/tmp/examplebinary',
    start: 0x200000000,
    end: 0x200000000 + 20,
    arch: 'x86_64',
  },
  {
    breakpadId: '100000000000000000000000000000A27',
    debugName: 'examplebinary2.pdb',
    name: 'examplebinary2',
    path: 'C:\\examplebinary2',
    debugPath: 'C:\\examplebinary2.pdb',
    start: 0x200000000 + 20,
    end: 0x200000000 + 40,
    arch: 'x86_64',
  },
];

const thread = {
  samples: {
    schema: {
      stack: 0,
      time: 1,
      responsiveness: 2,
      rss: 3,
      uss: 4,
      frameNumber: 5,
      power: 6,
    },
    data: [
      [1, 0, 0], // (root), 0x100000f84
      [2, 1, 0], // (root), 0x100000f84, 0x100001a45
      [2, 2, 0], // (root), 0x100000f84, 0x100001a45
      [3, 3, 0], // (root), 0x100000f84, Startup::XRE_Main
      [0, 4, 0], // (root)
      [1, 5, 0], // (root), 0x100000f84
      [4, 6, 0], // (root), 0x100000f84, frobnicate
    ],
  },
  stackTable: {
    schema: { prefix: 0, frame: 1 },
    data: [
      [null, 0], // (root)
      [0, 1], // (root), 0x100000f84
      [1, 2], // (root), 0x100000f84, 0x100001a45
      [1, 3], // (root), 0x100000f84, Startup::XRE_Main
      [1, 4], // (root), 0x100000f84, frobnicate
    ],
  },
  frameTable: {
    schema: {
      location: 0,
      implementation: 1,
      optimizations: 2,
      line: 3,
      category: 4,
    },
    data: [
      [0], // (root)
      [1], // 0x100000f84
      [2], // 0x100001a45
      [3, null, null, 4391, 16], // Startup::XRE_Main, line 4391, category 16
      [7, 6, null, 34], // frobnicate, implementation 'baseline', line 34
    ],
  },
  markers: {
    schema: { name: 0, time: 1, data: 2 },
    data: [
      // Please keep the next marker at the start if you add more markers in
      // this structure.
      // We want to test tracing markers with the missing "start" marker. So
      // here is only the "end" marker without a matching "start" marker.
      [
        10, // Rasterize
        1,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      // This marker is filtered out
      [4, 2, { category: 'VsyncTimestamp', vsync: 0 }],
      [
        5, // Reflow
        3,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        10, // Rasterize
        4,
        {
          category: 'Paint',
          interval: 'start',
          type: 'tracing',
        },
      ],
      [
        10, // Rasterize
        5,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        8,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        9,
        11, // Note: this marker is out of order on purpose, to test we correctly sort
        {
          // MinorGC at time 11ms from 11ms to 12ms
          startTime: 11,
          endTime: 12,
        },
      ],
      [
        8,
        9,
        {
          // DOMEvent at time 9ms from 9ms to 10ms
          startTime: 9,
          endTime: 10,
          timeStamp: 1,
          type: 'mouseout',
          phase: 3,
        },
      ],
      [
        11, // UserTiming
        12,
        {
          startTime: 12,
          endTime: 13,
          type: 'UserTiming',
          name: 'processing-thread',
          entryType: 'measure',
        },
      ],
      // Nested reflows
      [
        5, // Reflow
        13,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        14,
        {
          category: 'Paint',
          interval: 'start',
          stack: {
            markers: { schema: { name: 0, time: 1, data: 2 }, data: [] },
            name: 'SyncProfile',
            samples: {
              schema: {
                stack: 0,
                time: 1,
                responsiveness: 2,
                rss: 3,
                uss: 4,
                frameNumber: 5,
                power: 6,
              },
              data: [[2, 1]], // (root), 0x100000f84, 0x100001a45
            },
          },
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        15,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      [
        5, // Reflow
        18,
        {
          category: 'Paint',
          interval: 'end',
          type: 'tracing',
        },
      ],
      // Starting a tracing but never finishing it.
      // Please keep it at the end if you add more markers in this structure
      [
        10, // Rasterize
        20,
        {
          category: 'Paint',
          interval: 'start',
          type: 'tracing',
        },
      ],
    ],
  },
  stringTable: [
    '(root)',
    '0x100000f84',
    '0x100001a45',
    'Startup::XRE_Main',
    'VsyncTimestamp',
    'Reflow',
    'baseline',
    'frobnicate (chrome://blargh:34)',
    'DOMEvent',
    'MinorGC',
    'Rasterize',
    'UserTiming',
  ],
};

const parentProcessMeta = {
  abi: 'x86_64-gcc3',
  interval: 1,
  misc: 'rv:48.0',
  oscpu: 'Intel Mac OS X 10.11',
  platform: 'Macintosh',
  processType: 0,
  product: 'Firefox',
  stackwalk: 1,
  startTime: 1460221352723.438,
  toolkit: 'cocoa',
  version: 5,
};

const contentProcessMeta = Object.assign({}, parentProcessMeta, {
  processType: 2,
  startTime: parentProcessMeta.startTime + 1000, // content process was launched 1 second after parent process
});

const contentProcessProfile = {
  meta: contentProcessMeta,
  libs: [contentProcessBinary].concat(extraBinaries), // libs are stringified in the Gecko profile
  threads: [Object.assign({ name: 'GeckoMain', processType: 'tab' }, thread)],
  processes: [],
};

const profile = {
  meta: parentProcessMeta,
  libs: [parentProcessBinary].concat(extraBinaries),
  threads: [
    Object.assign({ name: 'GeckoMain', processType: 'default' }, thread),
    Object.assign(
      { name: 'Compositor', processType: 'default' },
      cloneDeep(thread)
    ),
  ],
  processes: [contentProcessProfile],
};

export default function createProfile() {
  return cloneDeep(profile);
}
<|endoftext|>


<commit_before>import assert from 'assert';
import toBuffer from 'concat-stream';
import { getBase64DataURI, parseDataURI } from 'dauria';
import makeDebug from 'debug';
import errors from 'feathers-errors';
import mimeTypes from 'mime-types';
import { Service, createService, transform } from 'mostly-feathers-mongoose';
import request from 'request';

import BlobModel from '~/models/blob-model';
import defaultHooks from './blob-hooks';
import { fromBuffer, bufferToHash } from './util';

const debug = makeDebug('playing:content-services:blob');

const defaultOptions = {
  name: 'blobs',
  blobs: {
    fileCDN: '/file/'
  }
};

class BlobService extends Service {
  constructor(options) {
    options = Object.assign({}, defaultOptions, options);
    super(options);
  }

  setup(app) {
    super.setup(app);
    this.storage = app.get('storage');
    if (!this.storage) {
      throw new Error('app context `storage` must be provided');
    }
    this.hooks(defaultHooks(this.options));
  }

  get(id, params) {
    let [batchId, index] = id.split('.');
    debug('get', batchId, index);

    const readBlob = (blob) => {
      debug('readBlob', blob);
      return new Promise((resolve, reject) => {
        this.storage.createReadStream({
          key: blob.key
        })
        .on('error', reject)
        .pipe(toBuffer(buffer => {
          blob.file = getBase64DataURI(buffer, blob.mimetype);
          resolve(blob);
        }));
      });
    };

    if (index !== undefined) {
      return this._getBlob(batchId, index).then(blob =>
        params.query.embedded !== undefined? readBlob(blob) : blob);
    } else {
      return super.get(id, params).then(result => {
        debug('getBatch', result);
        result.blobs = transform(result.blobs);
        return result;
      });
    }
  }

  _getBlob(batchId, index) {
    return super.get(batchId).then(result => {
      if (!result) throw new Error('Blob batch id not exists');
      const batch = result.data || result;
      const blobs = batch.blobs || [];
      if (index >= blobs.length) throw new Error('Blob index out of range of the batch');
      return blobs[index];
    }).then(transform);
  }

  create(data, params) {
    return super.create(data, params);
  }

  update(id, data, params) {
    debug('update', id, data, params);
    assert(params.file, 'params file not provided.');
    assert(params.file.url, 'params file has no url.');

    const name = params.file.originalname;
    const mimetype = params.file.mimetype;
    const ext = mimeTypes.extension(mimetype);
    const size = params.file.size;

    const getBuffer = (url) => {
      const req = request.defaults({ encoding: null });
      return new Promise((resolve, reject) => {
        req.get(url, function(err, res, buffer) {
          if (err) return reject(err);
          return resolve(buffer);
        });
      });
    };

    const getBatch = (id) => {
      return super.get(id).then(result => {
        if (!result) throw new Error('Blob batch id not exists');
        return result.data || result;
      });
    };

    const writeBlob = ([batch, buffer]) => {
      console.log('######writeBlob', batch, buffer);
      batch.blobs = batch.blobs || [];
      const index = data.index || batch.blobs.length;
      const vender = data.vender || 'file';
      const key = `${id}.${index}.${ext}`;
      return new Promise((resolve, reject) => {
        fromBuffer(buffer)
          .pipe(this.storage.createWriteStream({
            key, name, vender, mimetype, size
          }, (error) => {
            if (error) return reject(error);
            let blob = {
              index, name, key, vender, mimetype, size
            };
            if (index < batch.blobs.length) {
              batch.blobs[index] = blob;
            } else {
              batch.blobs.push(blob);
            }
            return resolve(batch.blobs);
          }))
          .on('error', reject);
      });
    };

    const updateBlobs = (blobs) => {
      return super.patch(id, { blobs: blobs }).then(batch => {
        return batch.blobs && batch.blobs[batch.blobs.length - 1];
      });
    };

    return Promise.all([
      getBatch(id),
      getBuffer(params.file.url)
    ])
    .then(writeBlob)
    .then(updateBlobs);
  }

  patch(id, data, params) {
    return super.update(id, data, params);
  }

  remove (id) {
    let [batchId, index] = id.split('.');
    debug('remove', batchId, index);

    const removeBlob = (blob) => {
      debug('remove blob', blob);
      return new Promise((resolve, reject) => {
        this.storage.remove({
          key: blob.key
        }, error => error ? reject(error) : resolve(blob));
      });
    };

    if (index !== undefined) {
      return this._getBlob(batchId, index).then(removeBlob);
    } else {
      return super.remove(batchId);
    }
  }

  attachOnDocument(id, data, params, original) {
    assert(data.context && data.context.currentDocument, 'context.currentDocument not provided.');
    assert(data.context && data.context.documentType, 'context.documentType not provided.');

    const documents = this.app.service('documents');

    let blobs = (original.blobs || []).map((blob) => {
      blob.batch = original.id;
      delete blob.id;
      return blob;
    });

    return documents.get(data.context.currentDocument).then((doc) => {
      if (!doc) throw new Error('currentDocument not exists');
      let files = (doc.files || []).concat(blobs);
      debug('attachOnDocument', files);
      return documents.patch(doc.id, { files: files });
    });
  }

  removeFromDocument(id, data, params, orignal) {
    assert(data.context && data.context.currentDocument, 'context.currentDocument not provided.');
    assert(data.context && data.context.documentType, 'context.documentType not provided.');
    assert(data.xpath, 'data.xpath not provided.');

    const documents = this.app.service('documents');

    return documents.get(data.context.currentDocument).then((doc) => {
      if (!doc) throw new Error('currentDocument not exists');
      if (data.xpath.startsWith('files')) {
        let [xpath, index] = data.xpath.split('/');
        let files = (doc.files || []).filter((blob) => {
          return blob.index !== parseInt(index);
        });
        debug('removeFromDocument', xpath, index, files);
        return documents.patch(doc.id, { files: files });
      } else {
        let xpath = data.xpath;
        debug('removeFromDocument', xpath);
        return documents.patch(doc.id, { [xpath]: null });
      }
    });
  }
}

export default function init(app, options) {
  options = Object.assign({ ModelName: 'blob' }, options);
  return createService(app, BlobService, BlobModel, options);
}

init.Service = BlobService;

<commit_msg>remove console log<commit_after>import assert from 'assert';
import toBuffer from 'concat-stream';
import { getBase64DataURI, parseDataURI } from 'dauria';
import makeDebug from 'debug';
import errors from 'feathers-errors';
import mimeTypes from 'mime-types';
import { Service, createService, transform } from 'mostly-feathers-mongoose';
import request from 'request';

import BlobModel from '~/models/blob-model';
import defaultHooks from './blob-hooks';
import { fromBuffer, bufferToHash } from './util';

const debug = makeDebug('playing:content-services:blob');

const defaultOptions = {
  name: 'blobs',
  blobs: {
    fileCDN: '/file/'
  }
};

class BlobService extends Service {
  constructor(options) {
    options = Object.assign({}, defaultOptions, options);
    super(options);
  }

  setup(app) {
    super.setup(app);
    this.storage = app.get('storage');
    if (!this.storage) {
      throw new Error('app context `storage` must be provided');
    }
    this.hooks(defaultHooks(this.options));
  }

  get(id, params) {
    let [batchId, index] = id.split('.');
    debug('get', batchId, index);

    const readBlob = (blob) => {
      debug('readBlob', blob);
      return new Promise((resolve, reject) => {
        this.storage.createReadStream({
          key: blob.key
        })
        .on('error', reject)
        .pipe(toBuffer(buffer => {
          blob.file = getBase64DataURI(buffer, blob.mimetype);
          resolve(blob);
        }));
      });
    };

    if (index !== undefined) {
      return this._getBlob(batchId, index).then(blob =>
        params.query.embedded !== undefined? readBlob(blob) : blob);
    } else {
      return super.get(id, params).then(result => {
        debug('getBatch', result);
        result.blobs = transform(result.blobs);
        return result;
      });
    }
  }

  _getBlob(batchId, index) {
    return super.get(batchId).then(result => {
      if (!result) throw new Error('Blob batch id not exists');
      const batch = result.data || result;
      const blobs = batch.blobs || [];
      if (index >= blobs.length) throw new Error('Blob index out of range of the batch');
      return blobs[index];
    }).then(transform);
  }

  create(data, params) {
    return super.create(data, params);
  }

  update(id, data, params) {
    debug('update', id, data, params);
    assert(params.file, 'params file not provided.');
    assert(params.file.url, 'params file has no url.');

    const name = params.file.originalname;
    const mimetype = params.file.mimetype;
    const ext = mimeTypes.extension(mimetype);
    const size = params.file.size;

    const getBuffer = (url) => {
      const req = request.defaults({ encoding: null });
      return new Promise((resolve, reject) => {
        req.get(url, function(err, res, buffer) {
          if (err) return reject(err);
          return resolve(buffer);
        });
      });
    };

    const getBatch = (id) => {
      return super.get(id).then(result => {
        if (!result) throw new Error('Blob batch id not exists');
        return result.data || result;
      });
    };

    const writeBlob = ([batch, buffer]) => {
      batch.blobs = batch.blobs || [];
      const index = data.index || batch.blobs.length;
      const vender = data.vender || 'file';
      const key = `${id}.${index}.${ext}`;
      return new Promise((resolve, reject) => {
        fromBuffer(buffer)
          .pipe(this.storage.createWriteStream({
            key, name, vender, mimetype, size
          }, (error) => {
            if (error) return reject(error);
            let blob = {
              index, name, key, vender, mimetype, size
            };
            if (index < batch.blobs.length) {
              batch.blobs[index] = blob;
            } else {
              batch.blobs.push(blob);
            }
            return resolve(batch.blobs);
          }))
          .on('error', reject);
      });
    };

    const updateBlobs = (blobs) => {
      return super.patch(id, { blobs: blobs }).then(batch => {
        return batch.blobs && batch.blobs[batch.blobs.length - 1];
      });
    };

    return Promise.all([
      getBatch(id),
      getBuffer(params.file.url)
    ])
    .then(writeBlob)
    .then(updateBlobs);
  }

  patch(id, data, params) {
    return super.update(id, data, params);
  }

  remove (id) {
    let [batchId, index] = id.split('.');
    debug('remove', batchId, index);

    const removeBlob = (blob) => {
      debug('remove blob', blob);
      return new Promise((resolve, reject) => {
        this.storage.remove({
          key: blob.key
        }, error => error ? reject(error) : resolve(blob));
      });
    };

    if (index !== undefined) {
      return this._getBlob(batchId, index).then(removeBlob);
    } else {
      return super.remove(batchId);
    }
  }

  attachOnDocument(id, data, params, original) {
    assert(data.context && data.context.currentDocument, 'context.currentDocument not provided.');
    assert(data.context && data.context.documentType, 'context.documentType not provided.');

    const documents = this.app.service('documents');

    let blobs = (original.blobs || []).map((blob) => {
      blob.batch = original.id;
      delete blob.id;
      return blob;
    });

    return documents.get(data.context.currentDocument).then((doc) => {
      if (!doc) throw new Error('currentDocument not exists');
      let files = (doc.files || []).concat(blobs);
      debug('attachOnDocument', files);
      return documents.patch(doc.id, { files: files });
    });
  }

  removeFromDocument(id, data, params, orignal) {
    assert(data.context && data.context.currentDocument, 'context.currentDocument not provided.');
    assert(data.context && data.context.documentType, 'context.documentType not provided.');
    assert(data.xpath, 'data.xpath not provided.');

    const documents = this.app.service('documents');

    return documents.get(data.context.currentDocument).then((doc) => {
      if (!doc) throw new Error('currentDocument not exists');
      if (data.xpath.startsWith('files')) {
        let [xpath, index] = data.xpath.split('/');
        let files = (doc.files || []).filter((blob) => {
          return blob.index !== parseInt(index);
        });
        debug('removeFromDocument', xpath, index, files);
        return documents.patch(doc.id, { files: files });
      } else {
        let xpath = data.xpath;
        debug('removeFromDocument', xpath);
        return documents.patch(doc.id, { [xpath]: null });
      }
    });
  }
}

export default function init(app, options) {
  options = Object.assign({ ModelName: 'blob' }, options);
  return createService(app, BlobService, BlobModel, options);
}

init.Service = BlobService;

<|endoftext|>


<commit_before>import fs from 'fs';
import { resolve } from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import OfflinePlugin from 'offline-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import WebpackCleanupPlugin from 'webpack-cleanup-plugin';
import cheerio from 'cheerio';
import findCacheDir from 'find-cache-dir';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import frameworkConfig from '../../shared/framework-config';
import logger from '../../shared/logger';
import projectMetadata from '../../shared/project-metadata';
import frameworkMetadata from '../../shared/framework-metadata';
import frameworkBabelRc from '../../shared/framework-babelrc';
import { resolveRoot, resolveFrameworkSource } from '../../shared/resolve';
import { isDev, isTest } from '../EnvUtil';
import selectWebpackModulePlugin from './selectWebpackModulePlugin';

function replaceBabelPreset(babelConfig) {

  for (const key of Object.keys(babelConfig)) {
    const val = babelConfig[key];

    if (Array.isArray(val)) {
      for (let i = 0; i < val.length; i++) {
        if (val[i] === 'es2015') {
          val[i] = ['es2015', { modules: false }];
        }
      }
    } else if (val && typeof val === 'object') {
      replaceBabelPreset(val);
    }
  }

  return babelConfig;
}

export default class WebpackBase {

  static SIDE_SERVER = 0;
  static SIDE_CLIENT = 1;

  isDev: boolean;
  isTest: boolean;
  cssLoaders: { test: string, loaders: string[] }[] = [];

  constructor(side: number) {
    this.isDev = isDev;
    this.isTest = isTest;
    this.side = side;

    this.addCssLoader('(sc|sa|c)ss', [
      this.cssLoaderModule,
      'postcss-loader',
      'sass-loader',
    ]);
  }

  get cssLoaderModule() {
    const cssOptions = 'css-loader?modules&importLoaders=1&camelCase';
    if (this.isDev) {
      return `${cssOptions}&localIdentName=[local]__[path][name]__[hash:base64:5]&sourceMap`;
    }

    // disable cssnano removing deprecated prefixes.
    return `${cssOptions}&-autoprefixer`;
  }

  buildConfig() {
    const config = {
      name: this.isServer() ? 'Server' : 'Client',
      entry: this.getEntry(),
      output: this.getOutput(),
      module: {
        loaders: this.buildLoaders(),
      },
      plugins: this.getPlugins(),
      devtool: this.getDevTools(),
      target: this.getTarget(),
      resolve: {
        modules: ['node_modules'],
        extensions: [
          '.js',
          '.jsx',
          '.react.js',
        ],
        mainFields: [
          'jsnext:module',
          'module',
          'jsnext:main',
          'main',
        ],
        alias: this.getAliases(),
      },
    };

    if (this.isServer()) {
      // exclude any absolute module (npm/node) from the build, except this module.
      // this module must be included otherwise the server build will have two
      // separate versions of the framework loaded.
      const anyAbsoluteExceptFramework = new RegExp(`^(?!${frameworkMetadata.name}(\\/.+)?$)[a-z\\-0-9]+(\\/.+)?$`);

      config.externals = [
        anyAbsoluteExceptFramework,
      ];
    } else {
      config.resolve.mainFields.unshift('web');
      config.resolve.mainFields.unshift('jsnext:web');
      config.resolve.mainFields.unshift('browser');
      config.resolve.mainFields.unshift('jsnext:browser');
    }

    return config;
  }

  buildLoaders() {

    // node_modules\/(?!reworkjs)

    const anyNodeModuleExceptFramework = new RegExp(`node_modules\\/(?!${frameworkMetadata.name})`);

    const loaders = [{
      test: /\.jsx?$/,
      loader: 'babel-loader',
      exclude: anyNodeModuleExceptFramework,
      query: replaceBabelPreset(this.getBabelConfig()),
    }, {
      test: /\.(eot|svg|ttf|woff|woff2)(\?.*$|$)/,
      loader: 'file-loader',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        'file-loader',
        `image-webpack?{
          progressive: true,
          optimizationLevel: 7,
          interlaced: false,
          pngquant: {
            quality: "65-90",
            speed: 4
          }
        }`,
      ],
    }, {
      test: /\.json$/,
      loader: 'json-loader',
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader?limit=10000',
    }].concat(this.buildCssLoaders());

    if (this.isDev) {
      loaders.push({
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'eslint-loader?{rules:{"no-console":1}}',
        enforce: 'pre',
      });
    }

    return loaders;
  }

  buildCssLoaders() {
    const loaders = [];

    for (const cssLoader of this.cssLoaders) {
      const loader = {};
      loader.test = cssLoader.test;

      if (this.isDev) {
        loader.loaders = ['isomorphic-style-loader', ...cssLoader.loaders];
      } else {
        loader.loader = ExtractTextPlugin.extract({
          fallbackLoader: 'isomorphic-style-loader',
          loader: cssLoader.loaders,
        });
      }

      loaders.push(loader);
    }

    return loaders;
  }

  getEntry(): string[] {
    // front-end entry point.
    const entry = [
      this.isServer()
        ? resolveFrameworkSource('server/index')
        : resolveFrameworkSource('client/index'),
    ];

    // front-end dev libs.
    if (this.isDev && !this.isServer()) {
      entry.unshift(
        // Necessary for hot reloading with IE
        'eventsource-polyfill',
        'webpack-hot-middleware/client',
        resolveRoot('lib/internals/dev-preamble.js'),
      );
    }

    return entry;
  }

  getPublicPath() {
    return '/';
  }

  getOutput() {
    // Output to build directory.
    const output = {
      path: `${frameworkConfig.directories.build}/webpack-${this.isServer() ? 'server' : 'client'}`,
      publicPath: this.getPublicPath(),
    };

    if (this.isServer()) {
      output.libraryTarget = 'commonjs2';
    }

    if (this.isDev) {
      // Don't use hashes in dev mode for better performance
      Object.assign(output, {
        filename: '[name].js',
        chunkFilename: '[name].chunk.js',

        // https://github.com/mxstbr/react-boilerplate/issues/443
        // publicPath: 'http://localhost:3000',
      });
    } else {
      // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
      Object.assign(output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].chunk.js',
      });
    }

    return output;
  }

  getTarget() {
    return this.isServer() ? 'node' : 'web';
  }

  getDevTools() {
    return this.isDev ? 'cheap-module-eval-source-map' : 'cheap-source-map';
  }

  getAliases() {

    return {
      // Framework configuration directories
      '@@pre-init': frameworkConfig['pre-init'],
      '@@main-component': frameworkConfig['entry-react'],
      '@@directories.routes': frameworkConfig.directories.routes,
      '@@directories.translations': frameworkConfig.directories.translations,
      '@@directories.providers': frameworkConfig.directories.providers,
      [frameworkMetadata.name]: resolveRoot(''),

      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    };
  }

  addCssLoader(fileExt: string | RegExp, loaders: string[]) {
    if (typeof fileExt === 'string') {
      fileExt = new RegExp(`\\.${fileExt}$`);
    }

    this.cssLoaders.push({
      test: fileExt,
      loaders,
    });
  }

  getPlugins() {
    const definePluginArg = {
      'process.env.BUILD_ENV': JSON.stringify(process.env.NODE_ENV), // eslint-disable-line no-process-env
      webpack_globals: {
        PROCESS_NAME: JSON.stringify(`${projectMetadata.name} (${this.isServer() ? 'server' : 'client'})`),
        SIDE: JSON.stringify(this.isServer() ? 'server' : 'client'),
        PROJECT_DIR: JSON.stringify(process.cwd()),
        ROOT_DIR: JSON.stringify(resolve(__dirname, '../../..')),
      },
      frameworkConfig: JSON.stringify(frameworkConfig),
      frameworkBabelrc: JSON.stringify(frameworkBabelRc),
      projectMetadata: JSON.stringify(projectMetadata),
      frameworkMetadata: JSON.stringify(frameworkMetadata),
    };

    if (!this.isServer()) {
      definePluginArg['process.env'] = {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV), // eslint-disable-line no-process-env
      };
    }

    const plugins = [
      new CopyWebpackPlugin([{
        from: { glob: `${frameworkConfig.directories.resources}/**/**/*` },
        to: './',
        toType: 'dir',
      }]),

      new webpack.DefinePlugin(definePluginArg),

      selectWebpackModulePlugin(),
    ];

    // TODO inject DLLs `
    // TODO https://github.com/diurnalist/chunk-manifest-webpack-plugin

    if (this.isDev) {
      plugins.push(
        // enable hot reloading.
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),

        // Watcher doesn't work well if you mistype casing in a path so we use
        // a plugin that prints an error when you attempt to do this.
        // See https://github.com/facebookincubator/create-react-app/issues/240
        new CaseSensitivePathsPlugin(),

        // If you require a missing module and then `npm install` it, you still have
        // to restart the development server for Webpack to discover it. This plugin
        // makes the discovery automatic so you don't have to restart.
        // See https://github.com/facebookincubator/create-react-app/issues/186
        // new WatchMissingNodeModulesPlugin(paths.appNodeModules), // TODO

        // Inject webpack bundle into HTML.
        new HtmlWebpackPlugin({
          inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
          templateContent: templateContent(),
        }),
      );
    } else {
      if (!this.isServer()) {
        // there is no need to optimise bundle sizes on the server
        plugins.push(
          new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            children: true,
            minChunks: 2,
            async: true,
          }),

          new webpack.optimize.UglifyJsPlugin({
            comments: false,
            compress: {
              warnings: false, // ...but do not show warnings in the console (there is a lot of them)
            },
          }),

          // OccurrenceOrderPlugin is needed for long-term caching to work properly.
          // See http://mxs.is/googmv
          new webpack.optimize.OccurrenceOrderPlugin(true),
        );
      }

      plugins.push(
        new WebpackCleanupPlugin(),

        new HtmlWebpackPlugin({
          inject: true,
          templateContent: templateContent(),
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }),

        // Extract the CSS into a seperate file
        new ExtractTextPlugin('[name].[contenthash].css'),

        // Put it in the end to capture all the HtmlWebpackPlugin's
        // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
        new OfflinePlugin({
          relativePaths: false,
          publicPath: this.getPublicPath(),

          // No need to cache .htaccess. See http://mxs.is/googmp,
          // this is applied before any match in `caches` section
          excludes: ['.htaccess'],

          caches: {
            main: [':rest:'],

            // All chunks marked as `additional`, loaded after main section
            // and do not prevent SW to install. Change to `optional` if
            // do not want them to be preloaded at all (cached only when first loaded)
            additional: ['*.chunk.js'],
          },

          // Removes warning for about `additional` section usage
          safeToUseOptionalCaches: true,

          AppCache: false,
        }),
      );
    }

    return plugins;
  }

  getBabelConfig() {
    const config = frameworkBabelRc;
    config.plugins = config.plugins || [];

    if (this.isDev) {
      config.presets.push('react-hmre');
    }

    // This is a feature of `babel-loader` for webpack (not Babel itself).
    // It enables caching results in ./node_modules/.cache/react-scripts/
    // directory for faster rebuilds. We use findCacheDir() because of:
    // https://github.com/facebookincubator/create-react-app/issues/483
    config.cacheDirectory = findCacheDir({
      name: frameworkMetadata.name,
    });

    return config;
  }

  isServer() {
    return this.side === WebpackBase.SIDE_SERVER;
  }
}

/**
 * We dynamically generate the HTML content in development so that the different
 * DLL Javascript files are loaded in script tags and available to our application.
 */
function templateContent() {

  const html = fs.readFileSync(frameworkConfig['entry-html']).toString();

  const doc = cheerio(html);
  const body = doc.find('body');
  if (body.length === 0) {
    throw new Error('Your HTML entry point does not contain a .');
  }

  const appDiv = doc.find('#app');
  if (appDiv.length === 0) {
    logger.info('Could not find #app in html entry point, inserting it.');
    body.append('

');
  }

  // const body = doc.find('body');
  // const dllNames = !dllPlugin.dlls ? ['frameworkDependencies'] : Object.keys(dllPlugin.dlls);
  // dllNames.forEach(dllName => body.append(`<script data-dll='true' src='/${dllName}.dll.js'></script>`));

  return doc.toString();
}
<commit_msg>Don’t generate a filename hash on the server build as it is not needed<commit_after>import fs from 'fs';
import { resolve } from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import OfflinePlugin from 'offline-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import WebpackCleanupPlugin from 'webpack-cleanup-plugin';
import cheerio from 'cheerio';
import findCacheDir from 'find-cache-dir';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import frameworkConfig from '../../shared/framework-config';
import logger from '../../shared/logger';
import projectMetadata from '../../shared/project-metadata';
import frameworkMetadata from '../../shared/framework-metadata';
import frameworkBabelRc from '../../shared/framework-babelrc';
import { resolveRoot, resolveFrameworkSource } from '../../shared/resolve';
import { isDev, isTest } from '../EnvUtil';
import selectWebpackModulePlugin from './selectWebpackModulePlugin';

function replaceBabelPreset(babelConfig) {

  for (const key of Object.keys(babelConfig)) {
    const val = babelConfig[key];

    if (Array.isArray(val)) {
      for (let i = 0; i < val.length; i++) {
        if (val[i] === 'es2015') {
          val[i] = ['es2015', { modules: false }];
        }
      }
    } else if (val && typeof val === 'object') {
      replaceBabelPreset(val);
    }
  }

  return babelConfig;
}

export default class WebpackBase {

  static SIDE_SERVER = 0;
  static SIDE_CLIENT = 1;

  isDev: boolean;
  isTest: boolean;
  cssLoaders: { test: string, loaders: string[] }[] = [];

  constructor(side: number) {
    this.isDev = isDev;
    this.isTest = isTest;
    this.side = side;

    this.addCssLoader('(sc|sa|c)ss', [
      this.cssLoaderModule,
      'postcss-loader',
      'sass-loader',
    ]);
  }

  get cssLoaderModule() {
    const cssOptions = 'css-loader?modules&importLoaders=1&camelCase';
    if (this.isDev) {
      return `${cssOptions}&localIdentName=[local]__[path][name]__[hash:base64:5]&sourceMap`;
    }

    // disable cssnano removing deprecated prefixes.
    return `${cssOptions}&-autoprefixer`;
  }

  buildConfig() {
    const config = {
      name: this.isServer() ? 'Server' : 'Client',
      entry: this.getEntry(),
      output: this.getOutput(),
      module: {
        loaders: this.buildLoaders(),
      },
      plugins: this.getPlugins(),
      devtool: this.getDevTools(),
      target: this.getTarget(),
      resolve: {
        modules: ['node_modules'],
        extensions: [
          '.js',
          '.jsx',
          '.react.js',
        ],
        mainFields: [
          'jsnext:module',
          'module',
          'jsnext:main',
          'main',
        ],
        alias: this.getAliases(),
      },
    };

    if (this.isServer()) {
      // exclude any absolute module (npm/node) from the build, except this module.
      // this module must be included otherwise the server build will have two
      // separate versions of the framework loaded.
      const anyAbsoluteExceptFramework = new RegExp(`^(?!${frameworkMetadata.name}(\\/.+)?$)[a-z\\-0-9]+(\\/.+)?$`);

      config.externals = [
        anyAbsoluteExceptFramework,
      ];
    } else {
      config.resolve.mainFields.unshift('web');
      config.resolve.mainFields.unshift('jsnext:web');
      config.resolve.mainFields.unshift('browser');
      config.resolve.mainFields.unshift('jsnext:browser');
    }

    return config;
  }

  buildLoaders() {

    // node_modules\/(?!reworkjs)

    const anyNodeModuleExceptFramework = new RegExp(`node_modules\\/(?!${frameworkMetadata.name})`);

    const loaders = [{
      test: /\.jsx?$/,
      loader: 'babel-loader',
      exclude: anyNodeModuleExceptFramework,
      query: replaceBabelPreset(this.getBabelConfig()),
    }, {
      test: /\.(eot|svg|ttf|woff|woff2)(\?.*$|$)/,
      loader: 'file-loader',
    }, {
      test: /\.(jpg|png|gif)$/,
      loaders: [
        'file-loader',
        `image-webpack?{
          progressive: true,
          optimizationLevel: 7,
          interlaced: false,
          pngquant: {
            quality: "65-90",
            speed: 4
          }
        }`,
      ],
    }, {
      test: /\.json$/,
      loader: 'json-loader',
    }, {
      test: /\.html$/,
      loader: 'html-loader',
    }, {
      test: /\.(mp4|webm)$/,
      loader: 'url-loader?limit=10000',
    }].concat(this.buildCssLoaders());

    if (this.isDev) {
      loaders.push({
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'eslint-loader?{rules:{"no-console":1}}',
        enforce: 'pre',
      });
    }

    return loaders;
  }

  buildCssLoaders() {
    const loaders = [];

    for (const cssLoader of this.cssLoaders) {
      const loader = {};
      loader.test = cssLoader.test;

      if (this.isDev) {
        loader.loaders = ['isomorphic-style-loader', ...cssLoader.loaders];
      } else {
        loader.loader = ExtractTextPlugin.extract({
          fallbackLoader: 'isomorphic-style-loader',
          loader: cssLoader.loaders,
        });
      }

      loaders.push(loader);
    }

    return loaders;
  }

  getEntry(): string[] {
    // front-end entry point.
    const entry = [
      this.isServer()
        ? resolveFrameworkSource('server/index')
        : resolveFrameworkSource('client/index'),
    ];

    // front-end dev libs.
    if (this.isDev && !this.isServer()) {
      entry.unshift(
        // Necessary for hot reloading with IE
        'eventsource-polyfill',
        'webpack-hot-middleware/client',
        resolveRoot('lib/internals/dev-preamble.js'),
      );
    }

    return entry;
  }

  getPublicPath() {
    return '/';
  }

  getOutput() {
    // Output to build directory.
    const output = {
      path: `${frameworkConfig.directories.build}/webpack-${this.isServer() ? 'server' : 'client'}`,
      publicPath: this.getPublicPath(),
    };

    if (this.isServer()) {
      output.libraryTarget = 'commonjs2';
    }

    if (this.isDev || this.isServer()) {
      // Don't use hashes in dev mode for better building performance.
      // Don't use them on the server because they're not needed and not user friendly.
      Object.assign(output, {
        filename: '[name].js',
        chunkFilename: '[name].chunk.js',

        // https://github.com/mxstbr/react-boilerplate/issues/443
        // publicPath: 'http://localhost:3000',
      });
    } else {
      // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets.
      Object.assign(output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].chunk.js',
      });
    }

    return output;
  }

  getTarget() {
    return this.isServer() ? 'node' : 'web';
  }

  getDevTools() {
    return this.isDev ? 'cheap-module-eval-source-map' : 'cheap-source-map';
  }

  getAliases() {

    return {
      // Framework configuration directories
      '@@pre-init': frameworkConfig['pre-init'],
      '@@main-component': frameworkConfig['entry-react'],
      '@@directories.routes': frameworkConfig.directories.routes,
      '@@directories.translations': frameworkConfig.directories.translations,
      '@@directories.providers': frameworkConfig.directories.providers,
      [frameworkMetadata.name]: resolveRoot(''),

      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    };
  }

  addCssLoader(fileExt: string | RegExp, loaders: string[]) {
    if (typeof fileExt === 'string') {
      fileExt = new RegExp(`\\.${fileExt}$`);
    }

    this.cssLoaders.push({
      test: fileExt,
      loaders,
    });
  }

  getPlugins() {
    const definePluginArg = {
      'process.env.BUILD_ENV': JSON.stringify(process.env.NODE_ENV), // eslint-disable-line no-process-env
      webpack_globals: {
        PROCESS_NAME: JSON.stringify(`${projectMetadata.name} (${this.isServer() ? 'server' : 'client'})`),
        SIDE: JSON.stringify(this.isServer() ? 'server' : 'client'),
        PROJECT_DIR: JSON.stringify(process.cwd()),
        ROOT_DIR: JSON.stringify(resolve(__dirname, '../../..')),
      },
      frameworkConfig: JSON.stringify(frameworkConfig),
      frameworkBabelrc: JSON.stringify(frameworkBabelRc),
      projectMetadata: JSON.stringify(projectMetadata),
      frameworkMetadata: JSON.stringify(frameworkMetadata),
    };

    if (!this.isServer()) {
      definePluginArg['process.env'] = {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV), // eslint-disable-line no-process-env
      };
    }

    const plugins = [
      new CopyWebpackPlugin([{
        from: { glob: `${frameworkConfig.directories.resources}/**/**/*` },
        to: './',
        toType: 'dir',
      }]),

      new webpack.DefinePlugin(definePluginArg),

      selectWebpackModulePlugin(),
    ];

    // TODO inject DLLs `
    // TODO https://github.com/diurnalist/chunk-manifest-webpack-plugin

    if (this.isDev) {
      plugins.push(
        // enable hot reloading.
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),

        // Watcher doesn't work well if you mistype casing in a path so we use
        // a plugin that prints an error when you attempt to do this.
        // See https://github.com/facebookincubator/create-react-app/issues/240
        new CaseSensitivePathsPlugin(),

        // If you require a missing module and then `npm install` it, you still have
        // to restart the development server for Webpack to discover it. This plugin
        // makes the discovery automatic so you don't have to restart.
        // See https://github.com/facebookincubator/create-react-app/issues/186
        // new WatchMissingNodeModulesPlugin(paths.appNodeModules), // TODO

        // Inject webpack bundle into HTML.
        new HtmlWebpackPlugin({
          inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
          templateContent: templateContent(),
        }),
      );
    } else {
      if (!this.isServer()) {
        // there is no need to optimise bundle sizes on the server
        plugins.push(
          new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            children: true,
            minChunks: 2,
            async: true,
          }),

          new webpack.optimize.UglifyJsPlugin({
            comments: false,
            compress: {
              warnings: false, // ...but do not show warnings in the console (there is a lot of them)
            },
          }),

          // OccurrenceOrderPlugin is needed for long-term caching to work properly.
          // See http://mxs.is/googmv
          new webpack.optimize.OccurrenceOrderPlugin(true),
        );
      }

      plugins.push(
        new WebpackCleanupPlugin(),

        new HtmlWebpackPlugin({
          inject: true,
          templateContent: templateContent(),
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }),

        // Extract the CSS into a seperate file
        new ExtractTextPlugin('[name].[contenthash].css'),

        // Put it in the end to capture all the HtmlWebpackPlugin's
        // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
        new OfflinePlugin({
          relativePaths: false,
          publicPath: this.getPublicPath(),

          // No need to cache .htaccess. See http://mxs.is/googmp,
          // this is applied before any match in `caches` section
          excludes: ['.htaccess'],

          caches: {
            main: [':rest:'],

            // All chunks marked as `additional`, loaded after main section
            // and do not prevent SW to install. Change to `optional` if
            // do not want them to be preloaded at all (cached only when first loaded)
            additional: ['*.chunk.js'],
          },

          // Removes warning for about `additional` section usage
          safeToUseOptionalCaches: true,

          AppCache: false,
        }),
      );
    }

    return plugins;
  }

  getBabelConfig() {
    const config = frameworkBabelRc;
    config.plugins = config.plugins || [];

    if (this.isDev) {
      config.presets.push('react-hmre');
    }

    // This is a feature of `babel-loader` for webpack (not Babel itself).
    // It enables caching results in ./node_modules/.cache/react-scripts/
    // directory for faster rebuilds. We use findCacheDir() because of:
    // https://github.com/facebookincubator/create-react-app/issues/483
    config.cacheDirectory = findCacheDir({
      name: frameworkMetadata.name,
    });

    return config;
  }

  isServer() {
    return this.side === WebpackBase.SIDE_SERVER;
  }
}

/**
 * We dynamically generate the HTML content in development so that the different
 * DLL Javascript files are loaded in script tags and available to our application.
 */
function templateContent() {

  const html = fs.readFileSync(frameworkConfig['entry-html']).toString();

  const doc = cheerio(html);
  const body = doc.find('body');
  if (body.length === 0) {
    throw new Error('Your HTML entry point does not contain a .');
  }

  const appDiv = doc.find('#app');
  if (appDiv.length === 0) {
    logger.info('Could not find #app in html entry point, inserting it.');
    body.append('

');
  }

  // const body = doc.find('body');
  // const dllNames = !dllPlugin.dlls ? ['frameworkDependencies'] : Object.keys(dllPlugin.dlls);
  // dllNames.forEach(dllName => body.append(`<script data-dll='true' src='/${dllName}.dll.js'></script>`));

  return doc.toString();
}
<|endoftext|>


<commit_before>var Key = require('../lib/Key');
var Address = require('../lib/Address');
var k = new Key();

//k.public = new Buffer('92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468', 'hex');
//k.public = new Buffer('0492eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468', 'hex');
//k.public = new Buffer('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455', 'hex');
//k.generatePubKey();
//debugger;
//console.log(k);

var Electrum = require('../lib/Electrum');
//92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468
var mpk = '92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468';
mpk = new Electrum(mpk);

var key1 = mpk.generatePubKey(0);
var addr1 = Address.fromPubKey(key1);

console.log(addr1.as('base58'));
<commit_msg>Electrum: removed dead code<commit_after>var Electrum = require('../lib/Electrum');
var Address = require('../lib/Address');

var mpk = '92eea4d2f5263651db9e3222caded1fd4c89772f79a7c03fb6afc00e9d2c9d2ed9b86c2c95fc1171e49163079dacb7f048b3c509a27a490e1df9e7128362d468';

mpk = new Electrum(mpk);

var key0 = mpk.generatePubKey(0);
var addr0 = Address.fromPubKey(key0);

console.log(addr0.as('base58'));
<|endoftext|>


<commit_before>const needsSetDisplayNameImport = Symbol('needs setDisplayName import')

module.exports = ({ types: t }) => {
  function setDisplayName (name, original) {
    return t.callExpression(
      t.callExpression(
        t.identifier('setDisplayName'),
        [t.stringLiteral(name)]
      ),
      [original]
    )
  }

  function wrapDeclaration (decl) {
    // Only wrap Component exports (i.e. starting with a capital letter)
    if (!/^[A-Z]/.test(decl.get('id').node.name)) {
      return
    }

    if (decl.node.init) {
      decl.get('init').replaceWith(
        setDisplayName(decl.node.id.name, decl.node.init)
      )
    }
  }

  function wrapDeclarations (declarations) {
    declarations.forEach(wrapDeclaration)
  }

  const importSetDisplayName =
    t.importDeclaration([
      t.importDefaultSpecifier(t.identifier('setDisplayName'))
    ], t.stringLiteral('recompose/setDisplayName'))

  return {
    visitor: {
      ExportNamedDeclaration (path, state) {
        state.file[needsSetDisplayNameImport] = true
        const declaration = path.get('declaration')
        if (declaration.node) {
          wrapDeclarations(declaration.get('declarations'))
        }
      },

      Program: {
        exit (path, state) {
          if (state.file[needsSetDisplayNameImport]) {
            path.unshiftContainer('body', importSetDisplayName)
          }
        }
      }
    }
  }
}
<commit_msg>Only set component displayNames in development mode.<commit_after>const needsSetDisplayNameImport = Symbol('needs setDisplayName import')

module.exports = ({ types: t }) => {
  function setDisplayName (name, original) {
    return t.callExpression(
      t.callExpression(
        t.identifier('setDisplayName'),
        [t.stringLiteral(name)]
      ),
      [original]
    )
  }

  function declareTempVariable (name, value) {
    return t.variableDeclaration('let', [
      t.variableDeclarator(name, value)
    ])
  }

  const isProduction = t.binaryExpression(
    '!==',
    t.memberExpression(
      t.memberExpression(t.identifier('process'), t.identifier('env')),
      t.identifier('NODE_ENV')
    ),
    t.stringLiteral('production')
  )

  function setDisplayNameIfProduction (exportName, tempName, initNode) {
    return [
      declareTempVariable(tempName, initNode),
      t.ifStatement(isProduction, t.expressionStatement(
        t.assignmentExpression(
          '=',
          tempName,
          setDisplayName(exportName.name, tempName)
        )
      ))
    ]
  }

  function wrapDeclaration (decl, state) {
    // Only wrap Component exports (i.e. starting with a capital letter)
    if (!/^[A-Z]/.test(decl.get('id').node.name)) {
      return
    }

    if (decl.node.init) {
      state.file[needsSetDisplayNameImport] = true

      const initNode = decl.node.init
      const exportName = decl.node.id

      const tempName = decl.parentPath.scope.generateUidIdentifier(exportName.name)
      decl.parentPath.parentPath.insertBefore(
        setDisplayNameIfProduction(exportName, tempName, initNode)
      )
      decl.get('init').replaceWith(tempName)
    }
  }

  function wrapDeclarations (declarations, state) {
    declarations.forEach((decl) => wrapDeclaration(decl, state))
  }

  const importSetDisplayName =
    t.importDeclaration([
      t.importDefaultSpecifier(t.identifier('setDisplayName'))
    ], t.stringLiteral('recompose/setDisplayName'))

  return {
    visitor: {
      ExportNamedDeclaration (path, state) {
        const declaration = path.get('declaration')
        if (declaration.node) {
          wrapDeclarations(declaration.get('declarations'), state)
        }
      },

      Program: {
        exit (path, state) {
          if (state.file[needsSetDisplayNameImport]) {
            path.unshiftContainer('body', importSetDisplayName)
          }
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/
var lazy_load = require('../src/cordova/lazy_load'),
    config = require('../src/cordova/config'),
    util = require('../src/cordova/util'),
    shell = require('shelljs'),
    npmconf = require('npmconf');
    path = require('path'),
    hooker = require('../src/cordova/hooker'),
    request = require('request'),
    fs = require('fs'),
    Q = require('q'),
    platforms = require('../src/cordova/platforms');

describe('lazy_load module', function() {
    var custom_path;
    var npm_cache_add;
    beforeEach(function() {
        custom_path = spyOn(config, 'has_custom_path').andReturn(false);
        npm_cache_add = spyOn(lazy_load, 'npm_cache_add').andReturn(Q(path.join('lib','dir')));
        fakeLazyLoad = function(id, platform, version) {
            if (platform == 'wp7' || platform == 'wp8') {
                return Q(path.join('lib', 'wp', id, version, platform));
            } else {
                return Q(path.join('lib', platform, id, version, platforms[platform] && platforms[platform].subdirectory ? platforms[platform].subdirectory : ''));
            }
        };
    });
    describe('cordova method (loads stock cordova libs)', function() {
        var custom;
        beforeEach(function() {
            custom = spyOn(lazy_load, 'custom').andReturn(Q(path.join('lib','dir')));
        });
        it('should throw if platform is not a stock cordova platform', function(done) {
            lazy_load.cordova('atari').then(function() {
                expect('this call').toEqual('to fail');
            }, function(err) {
                expect('' + err).toContain('Cordova library "atari" not recognized.');
            }).fin(done);
        });
        it('should invoke lazy_load.custom with appropriate url, platform, and version as specified in platforms manifest', function(done) {
            var url = platforms.android.url + ';a=snapshot;h=' + platforms.android.version + ';sf=tgz';
            lazy_load.cordova('android').then(function(dir) {
                expect(npm_cache_add).toHaveBeenCalled();
                expect(dir).toBeDefined();
                done();
            });
        });
    });

    describe('custom method (loads custom cordova libs)', function() {
        var exists, fire, rm;
        beforeEach(function() {
            spyOn(shell, 'mkdir');
            rm = spyOn(shell, 'rm');
            mv = spyOn(shell, 'mv');
            exists = spyOn(fs, 'existsSync').andReturn(false);
            readdir = spyOn(fs, 'readdirSync').andReturn(['somefile.txt']);
            fire = spyOn(hooker, 'fire').andReturn(Q());
        });

        it('should callback with no errors and not fire event hooks if library already exists', function(done) {
            exists.andReturn(true);
            var mock_platforms = {
                'platform X': {
                    id: 'some id',
                    url: 'http://some remote url',
                    version: 'three point five'
                }
            };
            lazy_load.custom(mock_platforms, 'platform X').then(function() {
                expect(fire).not.toHaveBeenCalled()
            }, function(err) {
                expect(err).not.toBeDefined();
            }).fin(done);
        });
        it('should callback with no errors and fire event hooks even if library already exists if the lib url is a local dir', function(done) {
            exists.andReturn(true);
            var mock_platforms = {
                'platform X': {
                    id: 'some id',
                    url: 'some local dir',
                    version: 'three point six'
                }
            };
            lazy_load.custom(mock_platforms, 'platform X').then(function() {
                expect(fire).not.toHaveBeenCalled()
            }, function(err) {
                expect(err).not.toBeDefined();
            }).fin(done);
        });

        describe('remote URLs for libraries', function() {
            var npmConfProxy;
            var req,
                load_spy,
                events = {},
                fakeRequest = {
                    on: jasmine.createSpy().andCallFake(function(event, cb) {
                        events[event] = cb;
                        return fakeRequest;
                    }),
                    pipe: jasmine.createSpy().andCallFake(function() { return fakeRequest; })
                };
            beforeEach(function() {
                npmConfProxy = null;
                events = {};
                fakeRequest.on.reset();
                fakeRequest.pipe.reset();
                req = spyOn(request, 'get').andCallFake(function() {
                    // Fire the 'end' event shortly.
                    setTimeout(function() {
                        events['end']();
                    }, 10);
                    return fakeRequest;
                });
                load_spy = spyOn(npmconf, 'load').andCallFake(function(cb) { cb(null, { get: function() { return npmConfProxy }}); });
            });

            it('should call request with appropriate url params', function(done) {
                var url = 'https://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
            it('should take into account https-proxy npm configuration var if exists for https:// calls', function(done) {
                var proxy = 'https://somelocalproxy.com';
                npmConfProxy = proxy;
                var url = 'https://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url,
                        proxy:proxy
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
            it('should take into account proxy npm config var if exists for http:// calls', function(done) {
                var proxy = 'http://somelocalproxy.com';
                npmConfProxy = proxy;
                var url = 'http://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url,
                        proxy:proxy
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
        });

        describe('local paths for libraries', function() {
            it('should return the local path, no symlink', function(done) {
                var mock_platforms = {
                    'X': {
                        id: 'id',
                        url: '/some/random/lib',
                        version: 'three point eight'
                    }
                };
                lazy_load.custom(mock_platforms, 'X').then(function(dir) {
                    expect(dir).toEqual('/some/random/lib');
                }, function(err) {
                    expect(err).toBeUndefined();
                }).fin(done);
            });
            it('should not file download hook', function(done) {
                var mock_platforms = {
                    'X': {
                        id: 'id',
                        url: '/some/random/lib',
                        version: 'three point nine'
                    }
                };
                lazy_load.custom(mock_platforms, 'X').then(function() {
                    expect(fire).not.toHaveBeenCalledWith('after_library_download', {platform:'X',url:'/some/random/lib',id:'id',version:'three point nine',path:'/some/random/lib', symlink:false});
                }, function(err) {
                    expect(err).toBeUndefined();
                }).fin(done);
            });
        });
    });

    describe('based_on_config method', function() {
        var cordova, custom;
        beforeEach(function() {
            cordova = spyOn(lazy_load, 'cordova').andReturn(Q());
            custom = spyOn(lazy_load, 'custom').andReturn(Q());
        });
        it('should invoke custom if a custom lib is specified', function(done) {
            var read = spyOn(config, 'read').andReturn({
                lib:{
                    maybe:{
                        url:'you or eye?',
                        id:'eye dee',
                        version:'four point twenty'
                    }
                }
            });
            var p = '/some/random/custom/path';
            custom_path.andReturn(p);
            custom.andCallFake(function (platforms, platform) {
                expect(platform).toEqual('maybe');
                expect(platforms[platform].url).toEqual('you or eye?');
                expect(platforms[platform].id).toEqual('eye dee');
                expect(platforms[platform].version).toEqual('four point twenty');
                return fakeLazyLoad(platforms[platform].id, platform, platforms[platform].version);
            });
            lazy_load.based_on_config('yup', 'maybe').then(function() {
                expect(custom).toHaveBeenCalled();
            }, function(err) {
                expect(err).toBeUndefined();
            }).fin(done);
        });
        it('should invoke cordova if no custom lib is specified', function(done) {
            lazy_load.based_on_config('yup', 'ios').then(function() {
                expect(cordova).toHaveBeenCalled();
            }, function(err) {
                expect(err).toBeUndefined();
            }).fin(done);
        });
    });
});
<commit_msg>fix(tests): cordova/lazy_load spec on Windows<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/
var lazy_load = require('../src/cordova/lazy_load'),
    config = require('../src/cordova/config'),
    util = require('../src/cordova/util'),
    shell = require('shelljs'),
    npmconf = require('npmconf');
    path = require('path'),
    hooker = require('../src/cordova/hooker'),
    request = require('request'),
    fs = require('fs'),
    Q = require('q'),
    platforms = require('../src/cordova/platforms');

describe('lazy_load module', function() {
    var custom_path;
    var npm_cache_add;
    beforeEach(function() {
        custom_path = spyOn(config, 'has_custom_path').andReturn(false);
        npm_cache_add = spyOn(lazy_load, 'npm_cache_add').andReturn(Q(path.join('lib','dir')));
        fakeLazyLoad = function(id, platform, version) {
            if (platform == 'wp7' || platform == 'wp8') {
                return Q(path.join('lib', 'wp', id, version, platform));
            } else {
                return Q(path.join('lib', platform, id, version, platforms[platform] && platforms[platform].subdirectory ? platforms[platform].subdirectory : ''));
            }
        };
    });
    describe('cordova method (loads stock cordova libs)', function() {
        var custom;
        beforeEach(function() {
            custom = spyOn(lazy_load, 'custom').andReturn(Q(path.join('lib','dir')));
        });
        it('should throw if platform is not a stock cordova platform', function(done) {
            lazy_load.cordova('atari').then(function() {
                expect('this call').toEqual('to fail');
            }, function(err) {
                expect('' + err).toContain('Cordova library "atari" not recognized.');
            }).fin(done);
        });
        it('should invoke lazy_load.custom with appropriate url, platform, and version as specified in platforms manifest', function(done) {
            var url = platforms.android.url + ';a=snapshot;h=' + platforms.android.version + ';sf=tgz';
            lazy_load.cordova('android').then(function(dir) {
                expect(npm_cache_add).toHaveBeenCalled();
                expect(dir).toBeDefined();
                done();
            });
        });
    });

    describe('custom method (loads custom cordova libs)', function() {
        var exists, fire, rm;
        beforeEach(function() {
            spyOn(shell, 'mkdir');
            rm = spyOn(shell, 'rm');
            mv = spyOn(shell, 'mv');
            exists = spyOn(fs, 'existsSync').andReturn(false);
            readdir = spyOn(fs, 'readdirSync').andReturn(['somefile.txt']);
            fire = spyOn(hooker, 'fire').andReturn(Q());
        });

        it('should callback with no errors and not fire event hooks if library already exists', function(done) {
            exists.andReturn(true);
            var mock_platforms = {
                'platform X': {
                    id: 'some id',
                    url: 'http://some remote url',
                    version: 'three point five'
                }
            };
            lazy_load.custom(mock_platforms, 'platform X').then(function() {
                expect(fire).not.toHaveBeenCalled()
            }, function(err) {
                expect(err).not.toBeDefined();
            }).fin(done);
        });
        it('should callback with no errors and fire event hooks even if library already exists if the lib url is a local dir', function(done) {
            exists.andReturn(true);
            var mock_platforms = {
                'platform X': {
                    id: 'some id',
                    url: 'some local dir',
                    version: 'three point six'
                }
            };
            lazy_load.custom(mock_platforms, 'platform X').then(function() {
                expect(fire).not.toHaveBeenCalled()
            }, function(err) {
                expect(err).not.toBeDefined();
            }).fin(done);
        });

        describe('remote URLs for libraries', function() {
            var npmConfProxy;
            var req,
                load_spy,
                events = {},
                fakeRequest = {
                    on: jasmine.createSpy().andCallFake(function(event, cb) {
                        events[event] = cb;
                        return fakeRequest;
                    }),
                    pipe: jasmine.createSpy().andCallFake(function() { return fakeRequest; })
                };
            beforeEach(function() {
                npmConfProxy = null;
                events = {};
                fakeRequest.on.reset();
                fakeRequest.pipe.reset();
                req = spyOn(request, 'get').andCallFake(function() {
                    // Fire the 'end' event shortly.
                    setTimeout(function() {
                        events['end']();
                    }, 10);
                    return fakeRequest;
                });
                load_spy = spyOn(npmconf, 'load').andCallFake(function(cb) { cb(null, { get: function() { return npmConfProxy }}); });
            });

            it('should call request with appropriate url params', function(done) {
                var url = 'https://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
            it('should take into account https-proxy npm configuration var if exists for https:// calls', function(done) {
                var proxy = 'https://somelocalproxy.com';
                npmConfProxy = proxy;
                var url = 'https://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url,
                        proxy:proxy
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
            it('should take into account proxy npm config var if exists for http:// calls', function(done) {
                var proxy = 'http://somelocalproxy.com';
                npmConfProxy = proxy;
                var url = 'http://github.com/apache/someplugin';
                var with_android_platform = {
                    'android': {
                        id: 'random',
                        url: url,
                        version: '1.0'
                    }
                };
                lazy_load.custom(with_android_platform, 'android').then(function() {
                    expect(req).toHaveBeenCalledWith({
                        url:url,
                        proxy:proxy
                    }, jasmine.any(Function));
                }, function(err) {
                    expect(err).not.toBeDefined();
                }).fin(done);
            });
        });

        describe('local paths for libraries', function() {
            it('should return the local path, no symlink', function(done) {
                var mock_platforms = {
                    'X': {
                        id: 'id',
                        url: '/some/random/lib',
                        version: 'three point eight'
                    }
                };
                lazy_load.custom(mock_platforms, 'X').then(function(dir) {
                    expect(path.normpath(dir)).toEqual('/some/random/lib');
                }, function(err) {
                    expect(err).toBeUndefined();
                }).fin(done);
            });
            it('should not file download hook', function(done) {
                var mock_platforms = {
                    'X': {
                        id: 'id',
                        url: '/some/random/lib',
                        version: 'three point nine'
                    }
                };
                lazy_load.custom(mock_platforms, 'X').then(function() {
                    expect(fire).not.toHaveBeenCalledWith('after_library_download', {platform:'X',url:'/some/random/lib',id:'id',version:'three point nine',path:'/some/random/lib', symlink:false});
                }, function(err) {
                    expect(err).toBeUndefined();
                }).fin(done);
            });
        });
    });

    describe('based_on_config method', function() {
        var cordova, custom;
        beforeEach(function() {
            cordova = spyOn(lazy_load, 'cordova').andReturn(Q());
            custom = spyOn(lazy_load, 'custom').andReturn(Q());
        });
        it('should invoke custom if a custom lib is specified', function(done) {
            var read = spyOn(config, 'read').andReturn({
                lib:{
                    maybe:{
                        url:'you or eye?',
                        id:'eye dee',
                        version:'four point twenty'
                    }
                }
            });
            var p = '/some/random/custom/path';
            custom_path.andReturn(p);
            custom.andCallFake(function (platforms, platform) {
                expect(platform).toEqual('maybe');
                expect(platforms[platform].url).toEqual('you or eye?');
                expect(platforms[platform].id).toEqual('eye dee');
                expect(platforms[platform].version).toEqual('four point twenty');
                return fakeLazyLoad(platforms[platform].id, platform, platforms[platform].version);
            });
            lazy_load.based_on_config('yup', 'maybe').then(function() {
                expect(custom).toHaveBeenCalled();
            }, function(err) {
                expect(err).toBeUndefined();
            }).fin(done);
        });
        it('should invoke cordova if no custom lib is specified', function(done) {
            lazy_load.based_on_config('yup', 'ios').then(function() {
                expect(cordova).toHaveBeenCalled();
            }, function(err) {
                expect(err).toBeUndefined();
            }).fin(done);
        });
    });
});
<|endoftext|>


<commit_before>class View {

	constructor(viewName, props) {

		this.name = viewName;
		this.element = document.createElement('div');
		this.element.classList.add('view');
		this.style = this.element.style;
		this.classList = this.element.classList;

		this.element.view = this;
		this.properties = props || {};

	}

	init() {

		return new Promise((resolve, reject) => {

			if (!View.routes[this.name]) {

				//get the complete view url
				let url = `views/${this.name}.js`;

				//check if the view actually exists using HttpRequest, so we have error handling
				let req = new XMLHttpRequest();
				req.open('GET', url, true);
				req.onload = () => {

					if (req.status >= 200 && req.status < 300) {

						//add the node
						var viewScriptNode = document.createElement('script');
						viewScriptNode.setAttribute('src', url);
						viewScriptNode.onload = () => {

							if (typeof View.routes[this.name] === 'function') {
								View.routes[this.name].call(this, this.element);
							}

						};
						document.head.appendChild(viewScriptNode);

						resolve(this);

					} else {
						reject(req.status);
					}

				};

				req.send();

			} else {

				View.routes[this.name].call(this, this.element);
				resolve(this);

			}

		});

	}

	appendChild(node) {
		return this.element.appendChild(node);
	}

	removeChild(node) {
		return this.element.removeChild(node);
	}

}

View.routes = {};

class ViewHolder { //extends EventEmitter

	constructor(node) {

		//super();
		this.element = node;
		this.parentViewHolder = null;

	}

	navigate(path, nonav) {

		let deepIndex = 0;
		/*
		if (this.parentViewHolder) {
			deepIndex = this.parentViewHolder.navigate(path, nonav);
		}
		*/

		history.pushState(null, path, path);
		if (!nonav) {

			this.purge();

			let paths = path.substring(1).split('/');
			let view = new View(paths[deepIndex], this.getParams());
			this.render(view);

			return view.init();

		}

		return Promise.resolve(this);

	}

	loadNav() {

		let path = window.location.pathname;
		if (!path || path === '/') {
			return Promise.reject('already root');
		}

		return this.navigate(path);

	}

	hookNavHandler() {

		window.addEventListener('popstate', (event) => {
			this.loadNav();
		});

	}

	hookHashHandler() {
		window.addEventListener('hashchange', () => this.loadHash());
	}

	getParams(str) {

		let queryParams = str;

		if (!queryParams) {

			if (!window.location.search) {
				return {};
			}
			queryParams = window.location.search.substring(1);

		}

		let viewParams = {};
		queryParams.forEach((val) => {

			let valSplit = val.split('=');
			let param = valSplit[0],
				value = '';

			if (valSplit.length > 1) {

				value = valSplit.slice(1, valSplit.length).join('=');
				viewParams[param] = value;

			}

		});

		return params;

	}

	loadHash() {

		this.purge();

		if (!window.location.hash) {
			return false;
		}

		let hash = window.location.hash.substring(1);
		let hashParams = hash.split('&');
		let viewName = hashParams[0];

		//populate the params
		let viewParams = this.getParams(hash);

		//create and activate the view
		this.render(new View(viewName, viewParams));

		return true;

	}

	render(view) {

		//this.emit('render', view);
		this.element.appendChild(view.element);

	}

	done(view) {
		//this.emit('done', view);
	}

	purge() {

		//this.emit('purge');
		while (this.element.firstChild) {
			this.element.removeChild(this.element.firstChild);
		}

	}

	remove(view) {

		//this.emit('remove', view);
		for (let i = 0; i < this.element.childNodes.length; i++) {

			if (this.element.childNodes[i].view === view) {

				this.element.removeChild(this.element.childNodes[i]);
				return true;

			}

		}

		return false;

	}

}
<commit_msg>(re-) enable EventEmitter on ViewHolder<commit_after>class View {

	constructor(viewName, props) {

		this.name = viewName;
		this.element = document.createElement('div');
		this.element.classList.add('view');
		this.style = this.element.style;
		this.classList = this.element.classList;

		this.element.view = this;
		this.properties = props || {};

	}

	init() {

		return new Promise((resolve, reject) => {

			if (!View.routes[this.name]) {

				//get the complete view url
				let url = `views/${this.name}.js`;

				//check if the view actually exists using HttpRequest, so we have error handling
				let req = new XMLHttpRequest();
				req.open('GET', url, true);
				req.onload = () => {

					if (req.status >= 200 && req.status < 300) {

						//add the node
						var viewScriptNode = document.createElement('script');
						viewScriptNode.setAttribute('src', url);
						viewScriptNode.onload = () => {

							if (typeof View.routes[this.name] === 'function') {
								View.routes[this.name].call(this, this.element);
							}

						};
						document.head.appendChild(viewScriptNode);

						resolve(this);

					} else {
						reject(req.status);
					}

				};

				req.send();

			} else {

				View.routes[this.name].call(this, this.element);
				resolve(this);

			}

		});

	}

	appendChild(node) {
		return this.element.appendChild(node);
	}

	removeChild(node) {
		return this.element.removeChild(node);
	}

}

View.routes = {};

class ViewHolder extends EventEmitter {

	constructor(node) {

		super();
		this.element = node;
		this.parentViewHolder = null;

	}

	navigate(path, nonav) {

		let deepIndex = 0;
		/*
		if (this.parentViewHolder) {
			deepIndex = this.parentViewHolder.navigate(path, nonav);
		}
		*/

		history.pushState(null, path, path);
		if (!nonav) {

			this.purge();

			let paths = path.substring(1).split('/');
			let view = new View(paths[deepIndex], this.getParams());
			this.render(view);

			return view.init();

		}

		return Promise.resolve(this);

	}

	loadNav() {

		let path = window.location.pathname;
		if (!path || path === '/') {
			return Promise.reject('already root');
		}

		return this.navigate(path);

	}

	hookNavHandler() {

		window.addEventListener('popstate', (event) => {
			this.loadNav();
		});

	}

	hookHashHandler() {
		window.addEventListener('hashchange', () => this.loadHash());
	}

	getParams(str) {

		let queryParams = str;

		if (!queryParams) {

			if (!window.location.search) {
				return {};
			}
			queryParams = window.location.search.substring(1);

		}

		let viewParams = {};
		queryParams.forEach((val) => {

			let valSplit = val.split('=');
			let param = valSplit[0],
				value = '';

			if (valSplit.length > 1) {

				value = valSplit.slice(1, valSplit.length).join('=');
				viewParams[param] = value;

			}

		});

		return params;

	}

	loadHash() {

		this.purge();

		if (!window.location.hash) {
			return false;
		}

		let hash = window.location.hash.substring(1);
		let hashParams = hash.split('&');
		let viewName = hashParams[0];

		//populate the params
		let viewParams = this.getParams(hash);

		//create and activate the view
		this.render(new View(viewName, viewParams));

		return true;

	}

	render(view) {

		this.emit('render', view);
		this.element.appendChild(view.element);

	}

	done(view) {
		//this.emit('done', view);
	}

	purge() {

		this.emit('purge');
		while (this.element.firstChild) {
			this.element.removeChild(this.element.firstChild);
		}

	}

	remove(view) {

		this.emit('remove', view);
		for (let i = 0; i < this.element.childNodes.length; i++) {

			if (this.element.childNodes[i].view === view) {

				this.element.removeChild(this.element.childNodes[i]);
				return true;

			}

		}

		return false;

	}

}
<|endoftext|>