mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 11:17:19 +08:00
409 lines
13 KiB
TypeScript
409 lines
13 KiB
TypeScript
import { spawn } from 'child_process';
|
|
import { NODE_MODULES_ROOT, SRC_ROOT, PROJECT_ROOT } from './constants';
|
|
import { src, dest } from 'gulp';
|
|
import { dirname, join } from 'path';
|
|
import { ensureDirSync, readdirSync, readFile, readFileSync, statSync, writeFile, writeFileSync } from 'fs-extra';
|
|
import { rollup } from 'rollup';
|
|
import { Replacer } from 'strip-function';
|
|
import * as commonjs from 'rollup-plugin-commonjs';
|
|
import * as multiEntry from 'rollup-plugin-multi-entry';
|
|
import * as nodeResolve from 'rollup-plugin-node-resolve';
|
|
import * as through from 'through2';
|
|
import * as uglifyPlugin from 'rollup-plugin-uglify';
|
|
import { argv } from 'yargs';
|
|
import * as path from 'path';
|
|
|
|
import { runWorker } from './utils/app-scripts-worker-client';
|
|
|
|
// These packages lack of types.
|
|
const resolveBin = require('resolve-bin');
|
|
|
|
export function mergeObjects(obj1: any, obj2: any ) {
|
|
if (! obj1) {
|
|
obj1 = {};
|
|
}
|
|
if (! obj2) {
|
|
obj2 = {};
|
|
}
|
|
var obj3 = {};
|
|
for (var attrname in obj1) {
|
|
(<any>obj3)[attrname] = obj1[attrname];
|
|
}
|
|
for (var attrname in obj2) {
|
|
(<any>obj3)[attrname] = obj2[attrname];
|
|
}
|
|
return obj3;
|
|
}
|
|
|
|
function getRootTsConfig(pathToReadFile): any {
|
|
const json = readFileSync(pathToReadFile);
|
|
|
|
let tsConfig = JSON.parse(json.toString());
|
|
return tsConfig;
|
|
}
|
|
|
|
export function createTempTsConfig(includeGlob: string[], target: string, moduleType: string, pathToReadFile: string, pathToWriteFile: string, overrideCompileOptions: any = null): any {
|
|
let config = getRootTsConfig(pathToReadFile);
|
|
if (!config.compilerOptions) {
|
|
config.compilerOptions = {};
|
|
}
|
|
// for now, we only compiling to same directory (no outdir)
|
|
if (config.compilerOptions && config.compilerOptions.outDir) {
|
|
delete config.compilerOptions.outDir;
|
|
}
|
|
if (config.compilerOptions) {
|
|
config.compilerOptions.module = moduleType;
|
|
config.compilerOptions.target = target;
|
|
}
|
|
config.include = includeGlob;
|
|
const componentsToExclude = [
|
|
'avatar',
|
|
'badge',
|
|
'button',
|
|
'card',
|
|
'card-content',
|
|
'card-header',
|
|
'card-title',
|
|
'gesture',
|
|
'icon',
|
|
'scroll',
|
|
'slides',
|
|
'toggle'
|
|
];
|
|
|
|
config.exclude = componentsToExclude.map(cmp => path.join(PROJECT_ROOT, `src/components/${cmp}`) + `/*.ts`)
|
|
.concat([
|
|
path.join(PROJECT_ROOT, 'src/components/index.ts'),
|
|
]);
|
|
|
|
console.log('\x1b[36m%s\x1b[0m', '\nExcluding the following core components from ng build:\n');
|
|
console.log('\x1b[33m%s\x1b[0m', componentsToExclude.join(', ') + '\n');
|
|
console.log('\x1b[36m%s\x1b[0m', 'Important: all web components should be included in the above list.\n');
|
|
|
|
if (overrideCompileOptions) {
|
|
config.compilerOptions = Object.assign(config.compilerOptions, overrideCompileOptions);
|
|
}
|
|
|
|
// TS represents paths internally with '/' and expects the tsconfig path to be in this format
|
|
let json = JSON.stringify(config, null, 2);
|
|
json = json.replace(/\\\\/g, '/');
|
|
|
|
const dirToCreate = dirname(pathToWriteFile);
|
|
ensureDirSync(dirToCreate);
|
|
writeFileSync(pathToWriteFile, json);
|
|
}
|
|
|
|
function removeDebugStatements() {
|
|
let replacer = new Replacer(['console.debug', 'console.time', 'console.timeEnd', 'assert', 'runInDev']);
|
|
return through.obj(function (file, encoding, callback) {
|
|
const content = file.contents.toString();
|
|
const cleanedJs = replacer.replace(content);
|
|
file.contents = new Buffer(cleanedJs, 'utf8');
|
|
callback(null, file);
|
|
});
|
|
}
|
|
|
|
export function copySourceToDest(destinationPath: string, excludeSpecs: boolean, excludeE2e: boolean, stripDebug: boolean) {
|
|
let glob = [`${SRC_ROOT}/**/*.ts`];
|
|
if (excludeSpecs) {
|
|
glob.push(`!${SRC_ROOT}/**/*.spec.ts`);
|
|
} else {
|
|
glob.push(`${SRC_ROOT}/**/*.spec.ts`);
|
|
}
|
|
if (excludeE2e) {
|
|
glob.push(`!${SRC_ROOT}/components/*/test/*/**/*.ts`);
|
|
}
|
|
let stream = src(glob);
|
|
if (stripDebug) {
|
|
console.log('Removing debug statements:', destinationPath);
|
|
stream = stream.pipe(removeDebugStatements());
|
|
}
|
|
|
|
return stream.pipe(dest(destinationPath));
|
|
}
|
|
|
|
export function copyGlobToDest(sourceGlob: string[], destPath: string) {
|
|
return src(sourceGlob).pipe(dest(destPath));
|
|
}
|
|
|
|
export function copyFonts(destinationPath: string) {
|
|
return src([
|
|
'src/fonts/*.+(ttf|woff|woff2)'
|
|
])
|
|
.pipe(dest(destinationPath));
|
|
}
|
|
|
|
export function compileSass(destinationPath: string) {
|
|
let sass = require('gulp-sass');
|
|
let autoprefixer = require('gulp-autoprefixer');
|
|
let cleanCSS = require('gulp-clean-css');
|
|
let rename = require('gulp-rename');
|
|
let buildConfig = require('../build/config');
|
|
|
|
return src([
|
|
join(SRC_ROOT, 'themes/ionic.build.default.scss'),
|
|
join(SRC_ROOT, 'themes/ionic.build.dark.scss')
|
|
])
|
|
.pipe(sass({
|
|
includePaths: []
|
|
}).on('error', sass.logError)
|
|
)
|
|
.pipe(autoprefixer(buildConfig.autoprefixer))
|
|
|
|
.pipe(rename(function (path) {
|
|
path.basename = path.basename.replace('.default', '');
|
|
path.basename = path.basename.replace('.build', '');
|
|
}))
|
|
|
|
.pipe(dest(destinationPath))
|
|
|
|
.pipe(cleanCSS())
|
|
|
|
.pipe(rename({
|
|
extname: '.min.css'
|
|
}))
|
|
|
|
.pipe(dest(destinationPath));
|
|
}
|
|
|
|
export function setSassIonicVersion(version: string) {
|
|
writeFileSync(join(SRC_ROOT, 'themes/version.scss'), `$ionic-version: "${version}";`);
|
|
}
|
|
|
|
export function copyFile(srcPath: string, destPath: string) {
|
|
const sourceData = readFileSync(srcPath);
|
|
writeFileSync(destPath, sourceData);
|
|
}
|
|
|
|
export function runNgc(pathToConfigFile: string, done: Function) {
|
|
let exec = require('child_process').exec;
|
|
let ngcPath = getBinaryPath('@angular/compiler-cli', 'ngc');
|
|
let shellCommand = `node --max_old_space_size=8096 ${ngcPath} -p ${pathToConfigFile}`;
|
|
|
|
exec(shellCommand, function(err, stdout, stderr) {
|
|
process.stdout.write(stdout);
|
|
process.stderr.write(stderr);
|
|
done(err);
|
|
});
|
|
}
|
|
|
|
export function runTsc(pathToConfigFile: string, done: Function) {
|
|
let exec = require('child_process').exec;
|
|
let tscPath = getBinaryPath('typescript', 'tsc');
|
|
let shellCommand = `node --max_old_space_size=8096 ${tscPath} -p ${pathToConfigFile}`;
|
|
|
|
exec(shellCommand, function (err, stdout, stderr) {
|
|
process.stdout.write(stdout);
|
|
process.stderr.write(stderr);
|
|
done(err);
|
|
});
|
|
}
|
|
|
|
export function runWebpack(pathToWebpackConfig: string, done: Function) {
|
|
let exec = require('child_process').exec;
|
|
let webpackPath = getBinaryPath('webpack');
|
|
let shellCommand = `node --max_old_space_size=8096 ${webpackPath} --config ${pathToWebpackConfig} --display-error-details`;
|
|
|
|
exec(shellCommand, function(err, stdout, stderr) {
|
|
process.stdout.write(stdout);
|
|
process.stderr.write(stderr);
|
|
done(err);
|
|
});
|
|
}
|
|
|
|
export function runAppScriptsServe(testOrDemoName: string, appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, coreCompilerFilePath: string, coreDir: string, sassConfigPath: string, copyConfigPath: string, watchConfigPath: string) {
|
|
console.log('Running ionic-app-scripts serve with', testOrDemoName);
|
|
const deepLinksDir = dirname(dirname(appNgModulePath));
|
|
let scriptArgs = [
|
|
'serve',
|
|
'--appEntryPoint', appEntryPoint,
|
|
'--appNgModulePath', appNgModulePath,
|
|
'--deepLinksDir', deepLinksDir,
|
|
'--srcDir', srcDir,
|
|
'--wwwDir', distDir,
|
|
'--tsconfig', tsConfig,
|
|
'--readConfigJson', 'false',
|
|
'--ionicAngularDir', ionicAngularDir,
|
|
'--coreCompilerFilePath', coreCompilerFilePath,
|
|
'--coreDir', coreDir,
|
|
'--sass', sassConfigPath,
|
|
'--copy', copyConfigPath,
|
|
'--enableLint', 'false'
|
|
];
|
|
|
|
if (watchConfigPath) {
|
|
scriptArgs.push('--watch');
|
|
scriptArgs.push(watchConfigPath);
|
|
}
|
|
|
|
const debug: boolean = argv.debug;
|
|
if (debug) {
|
|
scriptArgs.push('--debug');
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let pathToAppScripts = join(NODE_MODULES_ROOT, '.bin', 'ionic-app-scripts');
|
|
pathToAppScripts = process.platform === 'win32' ? pathToAppScripts + '.cmd' : pathToAppScripts;
|
|
|
|
const spawnedCommand = spawn(pathToAppScripts, scriptArgs, {stdio: 'inherit'});
|
|
console.log(`${pathToAppScripts} ${scriptArgs.join(' ')}`);
|
|
|
|
spawnedCommand.on('close', (code: number) => {
|
|
if (code === 0) {
|
|
return resolve();
|
|
}
|
|
reject(new Error('App-scripts failed with non-zero status code'));
|
|
});
|
|
});
|
|
}
|
|
|
|
export function runAppScriptsBuild(appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, coreCompilerFilePath: string, coreDir: string, sassConfigPath: string, copyConfigPath: string, isDev: boolean = false) {
|
|
const pathToAppScripts = join(NODE_MODULES_ROOT, '.bin', 'ionic-app-scripts');
|
|
const debug: boolean = argv.debug;
|
|
return runWorker(pathToAppScripts, debug, appEntryPoint, appNgModulePath, srcDir, distDir, tsConfig, ionicAngularDir, coreCompilerFilePath, coreDir, sassConfigPath, copyConfigPath, isDev);
|
|
}
|
|
|
|
/** Resolves the path for a node package executable. */
|
|
export function getBinaryPath(packageName: string, executable = packageName): string {
|
|
return resolveBin.sync(packageName, {executable});
|
|
}
|
|
|
|
export function deleteFiles(glob: string[], done: Function) {
|
|
let del = require('del');
|
|
del.sync(glob);
|
|
done();
|
|
}
|
|
|
|
export function createTimestamp() {
|
|
// YYYYMMDDHHMM
|
|
var d = new Date();
|
|
return d.getUTCFullYear() + // YYYY
|
|
('0' + (d.getUTCMonth() + 1)).slice(-2) + // MM
|
|
('0' + (d.getUTCDate())).slice(-2) + // DD
|
|
('0' + (d.getUTCHours())).slice(-2) + // HH
|
|
('0' + (d.getUTCMinutes())).slice(-2); // MM
|
|
}
|
|
|
|
export function writePolyfills(outputDirectory: string) {
|
|
const MODERN_ENTRIES = [
|
|
'node_modules/core-js/es6/array.js',
|
|
'node_modules/core-js/es6/date.js',
|
|
'node_modules/core-js/es6/function.js',
|
|
'node_modules/core-js/es6/map.js',
|
|
'node_modules/core-js/es6/number.js',
|
|
'node_modules/core-js/es6/object.js',
|
|
'node_modules/core-js/es6/parse-float.js',
|
|
'node_modules/core-js/es6/parse-int.js',
|
|
'node_modules/core-js/es6/set.js',
|
|
'node_modules/core-js/es6/string.js',
|
|
'node_modules/core-js/es7/reflect.js',
|
|
'node_modules/core-js/es6/reflect.js',
|
|
'node_modules/zone.js/dist/zone.js',
|
|
'scripts/polyfill/polyfill.dom.js'
|
|
];
|
|
|
|
const ALL_ENTRIES = [
|
|
'node_modules/core-js/es6/array.js',
|
|
'node_modules/core-js/es6/date.js',
|
|
'node_modules/core-js/es6/function.js',
|
|
'node_modules/core-js/es6/map.js',
|
|
'node_modules/core-js/es6/math.js',
|
|
'node_modules/core-js/es6/number.js',
|
|
'node_modules/core-js/es6/object.js',
|
|
'node_modules/core-js/es6/parse-float.js',
|
|
'node_modules/core-js/es6/parse-int.js',
|
|
'node_modules/core-js/es6/reflect.js',
|
|
'node_modules/core-js/es6/regexp.js',
|
|
'node_modules/core-js/es6/set.js',
|
|
'node_modules/core-js/es6/string.js',
|
|
'node_modules/core-js/es6/symbol.js',
|
|
'node_modules/core-js/es6/typed.js',
|
|
'node_modules/core-js/es6/weak-map.js',
|
|
'node_modules/core-js/es6/weak-set.js',
|
|
'node_modules/core-js/es7/reflect.js',
|
|
'node_modules/zone.js/dist/zone.js',
|
|
'scripts/polyfill/polyfill.dom.js'
|
|
];
|
|
|
|
const NG_ENTRIES = [
|
|
'node_modules/core-js/es7/reflect.js',
|
|
'node_modules/zone.js/dist/zone.js',
|
|
];
|
|
|
|
let promises = [];
|
|
promises.push(bundlePolyfill(MODERN_ENTRIES, join(outputDirectory, 'polyfills.modern.js')));
|
|
promises.push(bundlePolyfill(ALL_ENTRIES, join(outputDirectory, 'polyfills.js')));
|
|
promises.push(bundlePolyfill(NG_ENTRIES, join(outputDirectory, 'polyfills.ng.js')));
|
|
|
|
return Promise.all(promises);
|
|
};
|
|
|
|
function bundlePolyfill(pathsToIncludeInPolyfill: string[], outputPath: string) {
|
|
return rollup({
|
|
entry: pathsToIncludeInPolyfill,
|
|
plugins: [
|
|
multiEntry(),
|
|
nodeResolve({
|
|
module: true,
|
|
jsnext: true,
|
|
main: true
|
|
}),
|
|
commonjs(),
|
|
uglifyPlugin()
|
|
],
|
|
onwarn: () => {
|
|
return () => {};
|
|
}
|
|
}).then((bundle) => {
|
|
return bundle.write({
|
|
format: 'iife',
|
|
moduleName: 'MyBundle',
|
|
dest: outputPath
|
|
});
|
|
});
|
|
}
|
|
|
|
export function getFolderInfo() {
|
|
let componentName: string = null;
|
|
let componentTest: string = null;
|
|
const folder: string = argv.folder || argv.f;
|
|
if (folder && folder.length) {
|
|
const folderSplit = folder.split('/');
|
|
componentName = folderSplit[0];
|
|
componentTest = (folderSplit.length > 1 ? folderSplit[1] : 'basic');
|
|
}
|
|
return {
|
|
componentName: componentName,
|
|
componentTest: componentTest
|
|
};
|
|
}
|
|
|
|
export function getFolders(dir) {
|
|
return readdirSync(dir)
|
|
.filter(function(file) {
|
|
return statSync(join(dir, file)).isDirectory();
|
|
});
|
|
}
|
|
|
|
export function readFileAsync(filePath: string) {
|
|
return new Promise((resolve, reject) => {
|
|
readFile(filePath, (err: Error, buffer: Buffer) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
return resolve(buffer.toString());
|
|
});
|
|
});
|
|
}
|
|
|
|
export function writeFileAsync(filePath: string, fileContent: string) {
|
|
return new Promise((resolve, reject) => {
|
|
writeFile(filePath, fileContent, (err: Error) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
return resolve();
|
|
});
|
|
});
|
|
}
|