mirror of https://github.com/status-im/metro.git
packager: AssetServer: @flow
Reviewed By: davidaurelio Differential Revision: D4921672 fbshipit-source-id: 6405275bbd04550d7dd87cd5b8ef35a6cf5904f2
This commit is contained in:
parent
30b1e17ba5
commit
3e9063fba6
|
@ -5,16 +5,20 @@
|
|||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const declareOpts = require('../lib/declareOpts');
|
||||
const denodeify = require('denodeify');
|
||||
const fs = require('fs');
|
||||
const getAssetDataFromName = require('../node-haste').getAssetDataFromName;
|
||||
const path = require('path');
|
||||
|
||||
import type {AssetData} from '../node-haste/lib/getAssetDataFromName';
|
||||
|
||||
const createTimeoutPromise = timeout => new Promise((resolve, reject) => {
|
||||
setTimeout(reject, timeout, 'fs operation timeout');
|
||||
});
|
||||
|
@ -33,27 +37,24 @@ const stat = timeoutableDenodeify(fs.stat, FS_OP_TIMEOUT);
|
|||
const readDir = timeoutableDenodeify(fs.readdir, FS_OP_TIMEOUT);
|
||||
const readFile = timeoutableDenodeify(fs.readFile, FS_OP_TIMEOUT);
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
projectRoots: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
class AssetServer {
|
||||
constructor(options) {
|
||||
const opts = validateOpts(options);
|
||||
this._roots = opts.projectRoots;
|
||||
this._assetExts = opts.assetExts;
|
||||
|
||||
_roots: $ReadOnlyArray<string>;
|
||||
_assetExts: $ReadOnlyArray<string>;
|
||||
_hashes: Map<?string, string>;
|
||||
_files: Map<string, string>;
|
||||
|
||||
constructor(options: {|
|
||||
+assetExts: $ReadOnlyArray<string>,
|
||||
+projectRoots: $ReadOnlyArray<string>,
|
||||
|}) {
|
||||
this._roots = options.projectRoots;
|
||||
this._assetExts = options.assetExts;
|
||||
this._hashes = new Map();
|
||||
this._files = new Map();
|
||||
}
|
||||
|
||||
get(assetPath, platform = null) {
|
||||
get(assetPath: string, platform: ?string = null): Promise<Buffer> {
|
||||
const assetData = getAssetDataFromName(assetPath, new Set([platform]));
|
||||
return this._getAssetRecord(assetPath, platform).then(record => {
|
||||
for (let i = 0; i < record.scales.length; i++) {
|
||||
|
@ -66,39 +67,41 @@ class AssetServer {
|
|||
});
|
||||
}
|
||||
|
||||
getAssetData(assetPath, platform = null) {
|
||||
getAssetData(assetPath: string, platform: ?string = null): Promise<{|
|
||||
files: Array<string>,
|
||||
hash: string,
|
||||
name: string,
|
||||
scales: Array<number>,
|
||||
type: string,
|
||||
|}> {
|
||||
const nameData = getAssetDataFromName(assetPath, new Set([platform]));
|
||||
const data = {
|
||||
name: nameData.name,
|
||||
type: nameData.type,
|
||||
};
|
||||
const {name, type} = nameData;
|
||||
|
||||
return this._getAssetRecord(assetPath, platform).then(record => {
|
||||
data.scales = record.scales;
|
||||
data.files = record.files;
|
||||
const {scales, files} = record;
|
||||
|
||||
if (this._hashes.has(assetPath)) {
|
||||
data.hash = this._hashes.get(assetPath);
|
||||
return data;
|
||||
const hash = this._hashes.get(assetPath);
|
||||
if (hash != null) {
|
||||
return {files, hash, name, scales, type};
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const hash = crypto.createHash('md5');
|
||||
hashFiles(data.files.slice(), hash, error => {
|
||||
const hasher = crypto.createHash('md5');
|
||||
hashFiles(files.slice(), hasher, error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
data.hash = hash.digest('hex');
|
||||
this._hashes.set(assetPath, data.hash);
|
||||
data.files.forEach(f => this._files.set(f, assetPath));
|
||||
resolve(data);
|
||||
const freshHash = hasher.digest('hex');
|
||||
this._hashes.set(assetPath, freshHash);
|
||||
files.forEach(f => this._files.set(f, assetPath));
|
||||
resolve({files, hash: freshHash, name, scales, type});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onFileChange(type, filePath) {
|
||||
onFileChange(type: string, filePath: string) {
|
||||
this._hashes.delete(this._files.get(filePath));
|
||||
}
|
||||
|
||||
|
@ -113,7 +116,10 @@ class AssetServer {
|
|||
* 4. Then try to pick platform-specific asset records
|
||||
* 5. Then pick the closest resolution (rounding up) to the requested one
|
||||
*/
|
||||
_getAssetRecord(assetPath, platform = null) {
|
||||
_getAssetRecord(assetPath: string, platform: ?string = null): Promise<{|
|
||||
files: Array<string>,
|
||||
scales: Array<number>,
|
||||
|}> {
|
||||
const filename = path.basename(assetPath);
|
||||
|
||||
return (
|
||||
|
@ -135,14 +141,15 @@ class AssetServer {
|
|||
|
||||
let record;
|
||||
if (platform != null) {
|
||||
record = map[getAssetKey(assetData.assetName, platform)] ||
|
||||
map[assetData.assetName];
|
||||
record = map.get(getAssetKey(assetData.assetName, platform)) ||
|
||||
map.get(assetData.assetName);
|
||||
} else {
|
||||
record = map[assetData.assetName];
|
||||
record = map.get(assetData.assetName);
|
||||
}
|
||||
|
||||
if (!record) {
|
||||
throw new Error(
|
||||
/* $FlowFixMe: platform can be null */
|
||||
`Asset not found: ${assetPath} for platform: ${platform}`
|
||||
);
|
||||
}
|
||||
|
@ -152,7 +159,7 @@ class AssetServer {
|
|||
);
|
||||
}
|
||||
|
||||
_findRoot(roots, dir, debugInfoFile) {
|
||||
_findRoot(roots: $ReadOnlyArray<string>, dir: string, debugInfoFile: string): Promise<string> {
|
||||
return Promise.all(
|
||||
roots.map(root => {
|
||||
const absRoot = path.resolve(root);
|
||||
|
@ -185,18 +192,23 @@ class AssetServer {
|
|||
});
|
||||
}
|
||||
|
||||
_buildAssetMap(dir, files, platform) {
|
||||
const assets = files.map(this._getAssetDataFromName.bind(this, new Set([platform])));
|
||||
const map = Object.create(null);
|
||||
_buildAssetMap(dir: string, files: $ReadOnlyArray<string>, platform: ?string): Map<string, {|
|
||||
files: Array<string>,
|
||||
scales: Array<number>,
|
||||
|}> {
|
||||
const platforms = new Set(platform != null ? [platform] : []);
|
||||
const assets = files.map(this._getAssetDataFromName.bind(this, platforms));
|
||||
const map = new Map();
|
||||
assets.forEach(function(asset, i) {
|
||||
const file = files[i];
|
||||
const assetKey = getAssetKey(asset.assetName, asset.platform);
|
||||
let record = map[assetKey];
|
||||
let record = map.get(assetKey);
|
||||
if (!record) {
|
||||
record = map[assetKey] = {
|
||||
record = {
|
||||
scales: [],
|
||||
files: [],
|
||||
};
|
||||
map.set(assetKey, record);
|
||||
}
|
||||
|
||||
let insertIndex;
|
||||
|
@ -214,8 +226,8 @@ class AssetServer {
|
|||
return map;
|
||||
}
|
||||
|
||||
_getAssetDataFromName(platform, file) {
|
||||
return getAssetDataFromName(file, platform);
|
||||
_getAssetDataFromName(platforms: Set<string>, file: string): AssetData {
|
||||
return getAssetDataFromName(file, platforms);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,18 +59,18 @@ export type GetTransformOptions = (
|
|||
getDependencies: string => Promise<Array<string>>,
|
||||
) => Promise<ExtraTransformOptions>;
|
||||
|
||||
type Asset = {
|
||||
__packager_asset: boolean,
|
||||
fileSystemLocation: string,
|
||||
httpServerLocation: string,
|
||||
width: ?number,
|
||||
height: ?number,
|
||||
scales: number,
|
||||
files: Array<string>,
|
||||
hash: string,
|
||||
name: string,
|
||||
type: string,
|
||||
};
|
||||
type Asset = {|
|
||||
+__packager_asset: boolean,
|
||||
+fileSystemLocation: string,
|
||||
+httpServerLocation: string,
|
||||
+width: ?number,
|
||||
+height: ?number,
|
||||
+scales: Array<number>,
|
||||
+files: Array<string>,
|
||||
+hash: string,
|
||||
+name: string,
|
||||
+type: string,
|
||||
|};
|
||||
|
||||
const sizeOf = denodeify(imageSize);
|
||||
|
||||
|
@ -696,7 +696,7 @@ class Bundler {
|
|||
return {
|
||||
asset,
|
||||
code,
|
||||
meta: {dependencies, dependencyOffsets},
|
||||
meta: {dependencies, dependencyOffsets, preloaded: null},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -428,7 +428,7 @@ class Server {
|
|||
_rangeRequestMiddleware(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
data: string,
|
||||
data: string | Buffer,
|
||||
assetPath: string,
|
||||
) {
|
||||
if (req.headers && req.headers.range) {
|
||||
|
|
|
@ -14,13 +14,15 @@
|
|||
const getPlatformExtension = require('./getPlatformExtension');
|
||||
const path = require('path');
|
||||
|
||||
function getAssetDataFromName(filename: string, platforms: Set<string>): {|
|
||||
export type AssetData = {|
|
||||
assetName: string,
|
||||
name: string,
|
||||
platform: ?string,
|
||||
resolution: number,
|
||||
type: string,
|
||||
|} {
|
||||
|};
|
||||
|
||||
function getAssetDataFromName(filename: string, platforms: Set<string>): AssetData {
|
||||
const ext = path.extname(filename);
|
||||
const platformExt = getPlatformExtension(filename, platforms);
|
||||
|
||||
|
|
Loading…
Reference in New Issue