UNPKG

7.3 kBJavaScriptView Raw
1'use strict';
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 return new (P || (P = Promise))(function (resolve, reject) {
4 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7 step((generator = generator.apply(thisArg, _arguments || [])).next());
8 });
9};
10const util = require('util');
11const path = require('path');
12const fs = require('fs');
13const os = require('os');
14const execSync = require('child_process').execSync;
15const httpx = require('httpx');
16const kitx = require('kitx');
17const exists = util.promisify(fs.exists);
18const writeFile = util.promisify(fs.writeFile);
19const readFile = util.promisify(fs.readFile);
20const debug = require('debug')('fun:deps');
21const archiver = require('archiver');
22function read(readable, encoding) {
23 return new Promise((resolve, reject) => {
24 var onData, onError, onEnd;
25 var cleanup = function () {
26 // cleanup
27 readable.removeListener('error', onError);
28 readable.removeListener('data', onData);
29 readable.removeListener('end', onEnd);
30 };
31 const bufs = [];
32 var size = 0;
33 onData = function (buf) {
34 bufs.push(buf);
35 size += buf.length;
36 };
37 onError = function (err) {
38 cleanup();
39 reject(err);
40 };
41 onEnd = function () {
42 cleanup();
43 var buff = Buffer.concat(bufs, size);
44 if (encoding) {
45 const result = buff.toString(encoding);
46 return resolve(result);
47 }
48 resolve(buff);
49 };
50 readable.on('error', onError);
51 readable.on('data', onData);
52 readable.on('end', onEnd);
53 });
54}
55const zip = function (rootDir) {
56 const archive = archiver('zip', {
57 zlib: { level: 9 } // Sets the compression level.
58 });
59 // good practice to catch warnings (ie stat failures and other non-blocking errors)
60 archive.on('warning', function (err) {
61 console.log(err);
62 if (err.code === 'ENOENT') {
63 // log warning
64 }
65 else {
66 // throw error
67 throw err;
68 }
69 });
70 archive.on('error', (e) => console.log(e));
71 if (debug.enabled) {
72 archive.on('entry', function (entry) {
73 debug('entry: %j', entry);
74 });
75 archive.on('progress', (p) => {
76 console.log('progress');
77 debug('progress: %j', p);
78 });
79 }
80 archive.glob('node_modules/**', {
81 cwd: rootDir
82 });
83 archive.finalize();
84 return read(archive, 'base64');
85};
86function pkg(deps) {
87 return `{
88 "dependencies": ${JSON.stringify(deps)}
89}`;
90}
91function localBuild(data) {
92 return __awaiter(this, void 0, void 0, function* () {
93 const runtime = data.runtime;
94 const deps = pkg(data.dependencies);
95 const digest = kitx.md5(`${runtime}:${deps}`, 'hex');
96 var dir = `${os.tmpdir()}/${digest}`;
97 try {
98 fs.mkdirSync(dir);
99 }
100 catch (ex) {
101 // ignore error
102 }
103 const pkgPath = path.join(dir, 'package.json');
104 fs.writeFileSync(pkgPath, deps);
105 execSync('npm i --registry=https://registry.npm.taobao.org', {
106 cwd: dir
107 });
108 return zip(dir);
109 });
110}
111function remoteBuild(data) {
112 return __awaiter(this, void 0, void 0, function* () {
113 const url = `http://59766a59fa0b4391b703b51d97230296-cn-hangzhou.alicloudapi.com/build/${data.runtime}`;
114 const response = yield httpx.request(url, {
115 method: 'POST',
116 timeout: 60000,
117 headers: {
118 'content-type': 'application/json'
119 },
120 data: JSON.stringify(data)
121 });
122 debug('%j', data);
123 const statusCode = response.statusCode;
124 var body = yield httpx.read(response, 'utf8');
125 const headers = response.headers;
126 const contentType = headers['content-type'] || '';
127 if (contentType.startsWith('application/json')) {
128 body = JSON.parse(body);
129 }
130 if (statusCode !== 200) {
131 debug(response.headers);
132 debug('statusCode: %s', statusCode);
133 debug('build dependencies failed!');
134 let err = new Error(`${headers['x-ca-error-message']},` +
135 ` requestid: ${headers['x-ca-request-id']}`);
136 err.name = 'BuildError';
137 err.data = data;
138 throw err;
139 }
140 if (!body.ok) {
141 let err = new Error(`Build failed, ${body.message}`);
142 err.name = 'BuildError';
143 err.data = data;
144 throw err;
145 }
146 return body.data.zip;
147 });
148}
149function buildDeps(func, rootDir, type) {
150 return __awaiter(this, void 0, void 0, function* () {
151 const runtime = (func.runtime || 'nodejs4.4').replace('.', '_');
152 const buildType = type || process.env.BUILD_TYPE || 'remote';
153 if (buildType !== 'local' && buildType !== 'remote') {
154 throw new TypeError(`BUILD_TYPE must be 'local' or 'remote'.`);
155 }
156 // read package.json
157 const pkgPath = path.join(rootDir, 'package.json');
158 const hasPackageFile = yield exists(pkgPath);
159 if (!hasPackageFile) {
160 debug('The package.json inexists in Project, skipped.');
161 return;
162 }
163 const pkg = require(pkgPath);
164 const dependencies = pkg.dependencies || {};
165 if (Object.keys(dependencies).length === 0) {
166 debug('The package.json has not any dependencies in Project, skipped.');
167 return;
168 }
169 const data = {
170 runtime: runtime,
171 dependencies: dependencies
172 };
173 const stringToHash = `${runtime}:${JSON.stringify(dependencies)}`;
174 const hash = kitx.md5(stringToHash, 'hex');
175 const zipPath = path.join(rootDir, `node_modules_${hash}.zip`);
176 const md5Path = path.join(rootDir, `node_modules_${hash}.zip.md5`);
177 const hasZip = yield exists(zipPath);
178 const hasSign = yield exists(md5Path);
179 if (hasZip && hasSign) {
180 const zip = yield readFile(zipPath, 'base64');
181 const sign = yield readFile(md5Path, 'utf8');
182 if (sign === kitx.md5(zip, 'hex')) {
183 debug('The node_modules pre-compressed, skipped.');
184 return zip;
185 }
186 }
187 var base64;
188 if (buildType === 'remote') {
189 debug('build deps remotely.');
190 base64 = yield remoteBuild(data);
191 }
192 else {
193 debug('build deps locally.');
194 base64 = yield localBuild(data);
195 }
196 debug('build %j completed.', func);
197 const digest = kitx.md5(base64, 'hex');
198 yield writeFile(zipPath, base64, 'base64');
199 yield writeFile(md5Path, digest);
200 return base64;
201 });
202}
203module.exports = { buildDeps, read };