зеркало из https://github.com/microsoft/just.git
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:
Родитель
c267e7995c
Коммит
f4283da114
|
@ -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';
|
||||||
|
|
Загрузка…
Ссылка в новой задаче