Copy instructions task: advanced mode copy task (#95)

* adding a new copy-instructions task

* change file

* adding some instructions

* adding exports so consumers can use this task
This commit is contained in:
Kenneth Chau 2019-05-09 12:47:20 -07:00 коммит произвёл GitHub
Родитель c267e7995c
Коммит f4283da114
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 192 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "just-scripts",
"comment": "adds a new advanced copy task",
"type": "minor"
}
],
"packageName": "just-scripts",
"email": "kchau@microsoft.com"
}

Просмотреть файл

@ -0,0 +1,104 @@
import { join, basename, normalize } from 'path';
import { readdirSync } from 'fs';
import { arrayify } from './arrayify';
export interface CopyInstruction {
/**
* The path+filename of the source files. If more than one file is provided, the files will be merged in order
* and output to a file in the destination path.
*/
sourceFilePath: string | string[];
/**
* The path+filename of the destination file.
*/
destinationFilePath: string;
}
export interface CopyConfig {
copyInstructions: CopyInstruction[];
}
export interface LocalizedFileInfo {
/**
* The base path of where the localized files are located.
*/
baseDirectory: string;
/**
* The name of the localized file to be copied, without extension (.js or .min.js will be added).
*/
baseFileName: string;
}
/**
* Copies files into a destination directory with the same names.
* For example copyFilesToDestinationDirectory(['some/path/foo.js', 'bar.js'], 'dest/target') would result in the creation of
* files 'dest/target/foo.js' and 'dest/target/bar.js'.
*/
export function copyFilesToDestinationDirectory(sourceFilePaths: string | string[], destinationDirectory: string): CopyInstruction[] {
return arrayify(sourceFilePaths).map(sourceName => ({
sourceFilePath: normalize(sourceName),
destinationFilePath: join(destinationDirectory, basename(sourceName))
}));
}
/**
* Copies a file into a destination directory with a different name.
* For example copyFileToDestinationDirectoryWithRename('some/path/foo.js', 'bar.js', 'dest/target') would result in the creation of
* the file 'dest/target/bar.js'.
*/
export function copyFileToDestinationDirectoryWithRename(
sourceFilePath: string,
destinationName: string,
destinationDirectory: string
): CopyInstruction[] {
return [{ sourceFilePath, destinationFilePath: join(destinationDirectory, destinationName) }];
}
/**
* Copies files into a destination directory with different names.
* For example copyFilesToDestinationDirectoryWithRename([{sourceFilePath:'some/path/foo.js', destinationName:'bar.js'}], 'dest/target')
* would result in the creation of the file 'dest/target/bar.js'.
*/
export function copyFilesToDestinationDirectoryWithRename(
instrs: { sourceFilePath: string; destinationName: string }[],
destinationDirectory: string
): CopyInstruction[] {
return instrs.map(instr => ({
sourceFilePath: instr.sourceFilePath,
destinationFilePath: join(destinationDirectory, instr.destinationName)
}));
}
/**
* Copies all the files in a directory to the output folder.
* You can optionally provide a filter function that determines which files to copy.
*/
export function copyFilesInDirectory(
sourceDirectoryPath: string,
outputDirectoryPath: string,
filterFunction?: (file: string) => boolean
): CopyInstruction[] {
let files = readdirSync(sourceDirectoryPath);
if (filterFunction) {
files = files.filter(filterFunction);
}
return files.map(file => ({
sourceFilePath: join(sourceDirectoryPath, file),
destinationFilePath: join(outputDirectoryPath, file)
}));
}
/**
* Merges the contents of multiple files and places them in the output folder.
* This should only be used for text files and it should not be used for JavaScript
* files that we care about the sourcemap information since this does not merge sourcemaps.
*/
export function mergeFiles(sourceFilePaths: string[], destinationFilePath: string): CopyInstruction {
return {
sourceFilePath: sourceFilePaths,
destinationFilePath
};
}

Просмотреть файл

@ -0,0 +1,14 @@
import { copyFilesToDestinationDirectory } from '../CopyInstruction';
import { normalize } from 'path';
describe('CopyInstruction tests', () => {
it('copies files with the same name to the target directory', () => {
const result = copyFilesToDestinationDirectory(['files/foo.js', 'my/path/bar.js'], 'dist/lib/');
expect(result.length).toEqual(2);
expect(result[0].sourceFilePath).toEqual(normalize('files/foo.js'));
expect(result[0].destinationFilePath).toEqual(normalize('dist/lib/foo.js'));
expect(result[1].sourceFilePath).toEqual(normalize('my/path/bar.js'));
expect(result[1].destinationFilePath).toEqual(normalize('dist/lib/bar.js'));
});
});

Просмотреть файл

@ -0,0 +1,3 @@
export function arrayify<T>(obj: T | T[]): T[] {
return Array.isArray(obj) ? obj : [obj];
}

Просмотреть файл

@ -0,0 +1,35 @@
import { dirname } from 'path';
import { readFile, writeFile, copy, ensureDir } from 'fs-extra';
import { CopyInstruction, CopyConfig } from './CopyInstruction';
import { arrayify } from './arrayify';
import { uniqueValues } from './uniqueValues';
/**
* Function containing the core code for the copy task with a given config.
*/
export async function executeCopyInstructions(config: CopyConfig | undefined) {
if (config && config.copyInstructions) {
await createDirectories(config.copyInstructions);
await Promise.all(config.copyInstructions.map(executeSingleCopyInstruction));
}
}
function createDirectories(copyInstructions: CopyInstruction[]) {
return Promise.all(
uniqueValues(copyInstructions.map(instruction => dirname(instruction.destinationFilePath))).map(dirname => ensureDir(dirname))
);
}
function executeSingleCopyInstruction(copyInstruction: CopyInstruction) {
const sourceFileNames = arrayify(copyInstruction.sourceFilePath);
// source and dest are 1-to-1? perform binary copy.
if (sourceFileNames.length === 1) {
return copy(sourceFileNames[0], copyInstruction.destinationFilePath);
}
// perform text merge operation.
return Promise.all(sourceFileNames.map(fileName => readFile(fileName))).then(fileContents => {
writeFile(copyInstruction.destinationFilePath, fileContents.join('\n'));
});
}

Просмотреть файл

@ -0,0 +1,7 @@
/**
* Removes duplicate numbers from an array
* @param array - The array possibly containing duplicate values
*/
export function uniqueValues<T>(array: T[]): T[] {
return Array.from(new Set<T>(array));
}

Просмотреть файл

@ -21,3 +21,6 @@ export const webpackOverlays = {
import webpackMerge from 'webpack-merge'; import webpackMerge from 'webpack-merge';
export { webpackMerge }; export { webpackMerge };
import * as copyInstructions from './copy/CopyInstruction';
export { copyInstructions };

Просмотреть файл

@ -0,0 +1,14 @@
import { executeCopyInstructions } from '../copy/executeCopyInstructions';
import { CopyConfig } from '../copy/CopyInstruction';
/**
* This is an advanced copy task that allows more advanced usage beyond simple copies.
* It allows for copy renames. It takes in a config that can be generated dynamically with code at build time.
*
* @param config Copy instructions configuration
*/
export function copyInstructionsTask(config?: CopyConfig) {
return function copyInstructions() {
return executeCopyInstructions(config);
};
}

Просмотреть файл

@ -9,3 +9,4 @@ export * from './apiExtractorTask';
export * from './addPackageTask'; export * from './addPackageTask';
export * from './upgradeRepoTask'; export * from './upgradeRepoTask';
export * from './upgradeStackTask'; export * from './upgradeStackTask';
export * from './copyInstructionsTask';