Skip to content

Commit 9ac27b8

Browse files
committed
refactor(archive): switch types fs / jszip (wip)
1 parent 49d5529 commit 9ac27b8

File tree

12 files changed

+342
-125
lines changed

12 files changed

+342
-125
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ workspace.code-workspace
1010
### Testing
1111
__tests__/images
1212
coverage/
13+
__tests__/pptx-output/
14+
__tests__/pptx-cache/
1315

1416
### Customer related files
1517
__customer__/
16-
__tests__/pptx-output/
1718
src/dev-customer.ts
19+

src/automizer.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Slide } from './classes/slide';
2-
import { FileHelper } from './helper/file-helper';
32
import {
43
AutomizerParams,
54
AutomizerSummary,
5+
ArchiveParams,
66
SourceSlideIdentifier,
77
StatusTracker,
88
} from './types/types';
@@ -17,9 +17,7 @@ import path from 'path';
1717
import * as fs from 'fs';
1818
import { XmlHelper } from './helper/xml-helper';
1919
import ModifyPresentationHelper from './helper/modify-presentation-helper';
20-
import { contentTracker, ContentTracker } from './helper/content-tracker';
21-
import JSZip from 'jszip';
22-
import { CacheHelper } from './helper/cache-helper';
20+
import { ContentTracker } from './helper/content-tracker';
2321

2422
/**
2523
* Automizer
@@ -37,7 +35,7 @@ export default class Automizer implements IPresentationProps {
3735
templateDir: string;
3836
templateFallbackDir: string;
3937
outputDir: string;
40-
cacheDir: string;
38+
archiveParams: ArchiveParams;
4139
/**
4240
* Timer of automizer
4341
* @internal
@@ -62,10 +60,13 @@ export default class Automizer implements IPresentationProps {
6260
? params.templateFallbackDir + '/'
6361
: '';
6462
this.outputDir = params?.outputDir ? params.outputDir + '/' : '';
65-
this.cacheDir = params?.cacheDir
66-
? params.cacheDir
67-
: __dirname + '/../cache';
68-
CacheHelper.setDir(this.cacheDir);
63+
64+
this.archiveParams = <ArchiveParams>{
65+
mode: params?.archiveType?.mode || 'jszip',
66+
baseDir: params?.archiveType?.baseDir || __dirname + '/../cache',
67+
workDir: params?.archiveType?.workDir || 'tmp',
68+
cleanupWorkDir: params?.archiveType?.cleanupWorkDir,
69+
};
6970

7071
this.timer = Date.now();
7172
this.setStatusTracker(params?.statusTracker);
@@ -74,13 +75,19 @@ export default class Automizer implements IPresentationProps {
7475

7576
if (params.rootTemplate) {
7677
const location = this.getLocation(params.rootTemplate, 'template');
77-
this.rootTemplate = Template.import(location) as RootPresTemplate;
78+
this.rootTemplate = Template.import(
79+
location,
80+
this.archiveParams,
81+
) as RootPresTemplate;
7882
}
7983

8084
if (params.presTemplates) {
8185
this.params.presTemplates.forEach((file) => {
8286
const location = this.getLocation(file, 'template');
83-
const newTemplate = Template.import(location, file) as PresTemplate;
87+
const newTemplate = Template.import(
88+
location,
89+
this.archiveParams,
90+
) as PresTemplate;
8491
this.templates.push(newTemplate);
8592
});
8693
}
@@ -158,7 +165,12 @@ export default class Automizer implements IPresentationProps {
158165
return this;
159166
}
160167

161-
const newTemplate = Template.import(location, name);
168+
const importParams = {
169+
...this.archiveParams,
170+
name,
171+
};
172+
173+
const newTemplate = Template.import(location, importParams);
162174

163175
if (!this.isPresTemplate(newTemplate)) {
164176
this.rootTemplate = newTemplate;

src/classes/template.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { SlideInfo } from '../types/xml-types';
1010
import { XmlHelper } from '../helper/xml-helper';
1111
import { ContentTracker } from '../helper/content-tracker';
1212
import IArchive from '../interfaces/iarchive';
13+
import { ArchiveParams } from '../types/types';
1314

1415
export class Template implements ITemplate {
1516
/**
@@ -51,22 +52,22 @@ export class Template implements ITemplate {
5152
creationIds: SlideInfo[];
5253
existingSlides: number;
5354

54-
constructor(location: string) {
55+
constructor(location: string, params: ArchiveParams) {
5556
this.location = location;
56-
const archive = FileHelper.importArchive(location);
57+
const archive = FileHelper.importArchive(location, params);
5758
this.archive = archive;
5859
}
5960

6061
static import(
6162
location: string,
62-
name?: string,
63+
params: ArchiveParams,
6364
): PresTemplate | RootPresTemplate {
6465
let newTemplate: PresTemplate | RootPresTemplate;
65-
if (name) {
66-
newTemplate = new Template(location) as PresTemplate;
67-
newTemplate.name = name;
66+
if (params.name) {
67+
newTemplate = new Template(location, params) as PresTemplate;
68+
newTemplate.name = params.name;
6869
} else {
69-
newTemplate = new Template(location) as RootPresTemplate;
70+
newTemplate = new Template(location, params) as RootPresTemplate;
7071
newTemplate.slides = [];
7172
newTemplate.counter = [
7273
new CountHelper('slides', newTemplate),

src/dev.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import { vd } from './helper/general-helper';
22
import Automizer, { modify } from './index';
33

4+
const outputName = 'create-presentation-file-proxy.test.pptx';
45
const automizer = new Automizer({
56
templateDir: `${__dirname}/../__tests__/pptx-templates`,
67
outputDir: `${__dirname}/../__tests__/pptx-output`,
7-
// cacheDir: `${__dirname}/../__tests__/pptx-cache`,
8+
archiveType: {
9+
mode: 'fs',
10+
baseDir: `${__dirname}/../__tests__/pptx-cache`,
11+
workDir: outputName,
12+
cleanupWorkDir: true,
13+
},
814
removeExistingSlides: true,
915
cleanup: true,
1016
compression: 5,
@@ -40,9 +46,15 @@ const run = async () => {
4046
})
4147
.addSlide('charts', 1, (slide) => {
4248
slide.addElement('images', 2, 'imageJPG');
43-
// slide.modifyElement('BarsStacked', [modify.setChartData(dataSmaller)]);
49+
slide.modifyElement('BarsStacked', [modify.setChartData(dataSmaller)]);
50+
})
51+
.addSlide('charts', 1, (slide) => {
52+
slide.addElement('images', 2, 'imageJPG');
53+
slide.addElement('images', 2, 'imageSVG');
54+
slide.addElement('images', 2, 'imageSVG');
55+
slide.modifyElement('BarsStacked', [modify.setChartData(dataSmaller)]);
4456
})
45-
.write(`create-presentation-file-proxy.test.pptx`);
57+
.write(outputName);
4658

4759
vd(result.duration);
4860
// vd(pres.rootTemplate.content);

src/helper/archive/archive-fs.ts

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import Archive from './archive';
2+
import fs, { promises as fsPromises } from 'fs';
3+
import JSZip, { InputType } from 'jszip';
4+
import { ArchiveParams } from '../../types/types';
5+
import IArchive, { ArchivedFile } from '../../interfaces/iarchive';
6+
import { XmlDocument } from '../../types/xml-types';
7+
import ArchiveJszip from './archive-jszip';
8+
import {
9+
copyDir,
10+
ensureDirectoryExistence,
11+
exists,
12+
FileHelper,
13+
makeDirIfNotExists,
14+
} from '../file-helper';
15+
import { vd } from '../general-helper';
16+
import extract from 'extract-zip';
17+
18+
export default class ArchiveFs extends Archive implements IArchive {
19+
archive: boolean;
20+
params: ArchiveParams;
21+
dir: string = undefined;
22+
templatesDir: string;
23+
templateDir: string;
24+
outputDir: string;
25+
workDir: string;
26+
isActive: boolean;
27+
isRoot: boolean;
28+
29+
constructor(filename: string, params: ArchiveParams) {
30+
super(filename);
31+
32+
this.params = params;
33+
}
34+
35+
private async initialize() {
36+
this.setPaths();
37+
38+
await this.assertDirs();
39+
await this.extractFile(this.filename);
40+
41+
if (!this.params.name) {
42+
await this.prepareWorkDir(this.filename);
43+
this.isRoot = true;
44+
}
45+
46+
this.archive = true;
47+
48+
return this;
49+
}
50+
51+
setPaths(): void {
52+
this.dir = this.params.baseDir + '/';
53+
this.templatesDir = this.dir + 'templates' + '/';
54+
this.outputDir = this.dir + 'output' + '/';
55+
this.templateDir = undefined;
56+
this.workDir = this.outputDir + this.params.workDir + '/';
57+
}
58+
59+
async assertDirs(): Promise<void> {
60+
makeDirIfNotExists(this.dir);
61+
makeDirIfNotExists(this.templatesDir);
62+
makeDirIfNotExists(this.outputDir);
63+
makeDirIfNotExists(this.workDir);
64+
}
65+
66+
async extractFile(file: string) {
67+
const targetDir = this.getTemplateDir(file);
68+
69+
if (exists(targetDir)) {
70+
return;
71+
}
72+
73+
await extract(file, { dir: targetDir }).catch((err) => {
74+
throw err;
75+
});
76+
}
77+
78+
getTemplateDir(file: string): string {
79+
const info = FileHelper.getFileInfo(file);
80+
this.templateDir = this.templatesDir + info.base + '/';
81+
return this.templateDir;
82+
}
83+
84+
async prepareWorkDir(templateDir: string) {
85+
await this.cleanupWorkDir();
86+
87+
const fromTemplate = this.getTemplateDir(templateDir);
88+
await copyDir(fromTemplate, this.workDir);
89+
}
90+
91+
fileExists(file: string) {
92+
return exists(this.getPath(file));
93+
}
94+
95+
async folder(dir: string): Promise<ArchivedFile[]> {
96+
const path = this.getPath(dir);
97+
const files = [];
98+
99+
if (!exists(path)) {
100+
return files;
101+
}
102+
103+
let entries = await fsPromises.readdir(path, { withFileTypes: true });
104+
for (let entry of entries) {
105+
if (!entry.isDirectory()) {
106+
files.push({
107+
name: dir + '/' + entry.name,
108+
relativePath: entry.name,
109+
});
110+
}
111+
}
112+
113+
return files;
114+
}
115+
116+
async read(file: string): Promise<string | Buffer> {
117+
if (!this.archive) {
118+
await this.initialize();
119+
}
120+
121+
const path = this.getPath(file);
122+
return await fsPromises.readFile(path);
123+
}
124+
125+
getPath(file: string): string {
126+
if (this.isRoot) {
127+
return this.workDir + file;
128+
}
129+
return this.templateDir + file;
130+
}
131+
132+
async write(file: string, data: string | Buffer): Promise<this> {
133+
const filename = this.workDir + file;
134+
ensureDirectoryExistence(filename);
135+
await fsPromises.writeFile(filename, data);
136+
return this;
137+
}
138+
139+
async remove(file: string): Promise<void> {
140+
const path = this.getPath(file);
141+
if (exists(path)) {
142+
await fsPromises.unlink(path);
143+
}
144+
}
145+
146+
async output(location: string): Promise<void> {
147+
await this.writeBuffer(this);
148+
149+
let exec = require('child_process').exec;
150+
const command = `(cd ${this.workDir} && zip -r - .) > ${location}`;
151+
const script = await exec(command);
152+
153+
script.on('exit', async (code) => {
154+
if (this.params.cleanupWorkDir === true) {
155+
await this.cleanupWorkDir();
156+
}
157+
});
158+
}
159+
160+
async cleanupWorkDir(): Promise<void> {
161+
if (!exists(this.workDir)) {
162+
return;
163+
}
164+
165+
await fsPromises.rm(this.workDir, { recursive: true, force: true });
166+
}
167+
168+
async readXml(file: string): Promise<XmlDocument> {
169+
const isBuffered = this.fromBuffer(file);
170+
171+
if (!isBuffered) {
172+
const buffer = await this.read(file);
173+
if (!buffer) {
174+
throw 'no buffer: ' + file;
175+
}
176+
177+
const xmlString = buffer.toString();
178+
const XmlDocument = this.parseXml(xmlString);
179+
this.toBuffer(file, XmlDocument);
180+
181+
return XmlDocument;
182+
} else {
183+
return isBuffered.content;
184+
}
185+
}
186+
187+
writeXml(file: string, XmlDocument: XmlDocument): void {
188+
this.toBuffer(file, XmlDocument);
189+
}
190+
191+
/**
192+
* Used for worksheets only
193+
**/
194+
async extract(file: string): Promise<ArchiveJszip> {
195+
const contents = (await this.read(file)) as Buffer;
196+
const zip = new JSZip();
197+
const newArchive = new ArchiveJszip(file);
198+
newArchive.archive = await zip.loadAsync(contents as unknown as InputType);
199+
return newArchive;
200+
}
201+
}

0 commit comments

Comments
 (0)