1 | import path from 'node:path';
|
2 | import { validate } from 'schema-utils';
|
3 |
|
4 | import optionsSchema from './options-schema.js';
|
5 | import pExec from './utils/p-exec.js';
|
6 | import { copyAction, moveAction, mkdirAction, archiveAction, deleteAction } from './actions/index.js';
|
7 |
|
8 | const PLUGIN_NAME = 'FileManagerPlugin';
|
9 |
|
10 | const defaultOptions = {
|
11 | events: {
|
12 | onStart: [],
|
13 | onEnd: [],
|
14 | },
|
15 | runTasksInSeries: false,
|
16 | context: null,
|
17 | runOnceInWatchMode: false,
|
18 | };
|
19 |
|
20 | const resolvePaths = (action, context) => {
|
21 | return action.map((task) => {
|
22 | if (typeof task === 'string') {
|
23 | const source = task;
|
24 | return {
|
25 | source,
|
26 | absoluteSource: path.isAbsolute(source) ? source : path.join(context, source),
|
27 | };
|
28 | }
|
29 |
|
30 | const { source, destination } = task;
|
31 |
|
32 | if (!destination) {
|
33 | return {
|
34 | ...task,
|
35 | source,
|
36 | absoluteSource: path.isAbsolute(source) ? source : path.join(context, source),
|
37 | };
|
38 | }
|
39 |
|
40 | const toType = /(?:\\|\/)$/.test(destination) ? 'dir' : 'file';
|
41 |
|
42 | return {
|
43 | ...task,
|
44 | source,
|
45 | absoluteSource: path.isAbsolute(source) ? source : path.join(context, source),
|
46 | destination,
|
47 | absoluteDestination: path.isAbsolute(destination) ? destination : path.join(context, destination),
|
48 | toType,
|
49 | context,
|
50 | };
|
51 | });
|
52 | };
|
53 |
|
54 | class FileManagerPlugin {
|
55 | constructor(options) {
|
56 | validate(optionsSchema, options, {
|
57 | name: PLUGIN_NAME,
|
58 | baseDataPath: 'actions',
|
59 | });
|
60 |
|
61 | this.options = { ...defaultOptions, ...options };
|
62 | }
|
63 |
|
64 | async applyAction(action, actionParams) {
|
65 | const opts = {
|
66 | runTasksInSeries: this.options.runTasksInSeries,
|
67 | logger: this.logger,
|
68 | };
|
69 |
|
70 | await action(resolvePaths(actionParams, this.context), opts);
|
71 | }
|
72 |
|
73 | async run(event) {
|
74 | for (const actionType in event) {
|
75 | const action = event[actionType];
|
76 |
|
77 | switch (actionType) {
|
78 | case 'delete':
|
79 | await this.applyAction(deleteAction, action);
|
80 | break;
|
81 |
|
82 | case 'mkdir':
|
83 | await this.applyAction(mkdirAction, action);
|
84 | break;
|
85 |
|
86 | case 'copy':
|
87 | await this.applyAction(copyAction, action);
|
88 | break;
|
89 |
|
90 | case 'move':
|
91 | await this.applyAction(moveAction, action);
|
92 | break;
|
93 |
|
94 | case 'archive':
|
95 | await this.applyAction(archiveAction, action);
|
96 | break;
|
97 |
|
98 | default:
|
99 | throw Error('Unknown action');
|
100 | }
|
101 | }
|
102 | }
|
103 |
|
104 | async execute(eventName) {
|
105 | const { events } = this.options;
|
106 |
|
107 | if (Array.isArray(events[eventName])) {
|
108 | const eventsArr = events[eventName];
|
109 |
|
110 | await pExec(true, eventsArr, async (event) => await this.run(event));
|
111 | return;
|
112 | }
|
113 |
|
114 | const event = events[eventName];
|
115 | await this.run(event);
|
116 | }
|
117 |
|
118 | apply(compiler) {
|
119 | this.context = this.options.context || compiler.options.context;
|
120 | this.logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
|
121 |
|
122 | const onStart = async () => {
|
123 | await this.execute('onStart');
|
124 | };
|
125 |
|
126 | const onEnd = async () => {
|
127 | await this.execute('onEnd');
|
128 | };
|
129 |
|
130 | compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, onStart);
|
131 | compiler.hooks.afterEmit.tapPromise(PLUGIN_NAME, onEnd);
|
132 |
|
133 | let watchRunCount = 0;
|
134 | compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => {
|
135 | if (this.options.runOnceInWatchMode && watchRunCount > 0) {
|
136 | return;
|
137 | }
|
138 |
|
139 | ++watchRunCount;
|
140 | await onStart();
|
141 | });
|
142 | }
|
143 | }
|
144 |
|
145 | export default FileManagerPlugin;
|