Skip to content

Commit eed52d3

Browse files
authored
fix(export): make re-exporting possible (contentful-userland#108)
Both export methods (`toString` and `write`), require to modify the source project. When re-exporting the output changes depending on the order. With this fix, re-exporting is possible for both methods.
1 parent cfca775 commit eed52d3

File tree

2 files changed

+137
-80
lines changed

2 files changed

+137
-80
lines changed

src/cf-definitions-builder.ts

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,54 @@ export default class CFDefinitionsBuilder {
7070
return writeCallback(targetPath, file.getFullText());
7171
});
7272
await Promise.all(writePromises);
73+
this.removeIndexFile();
7374
};
7475

75-
public toString = (): string => this.mergeFile().getFullText();
76+
public toString = (): string => {
77+
const mergeFileName = 'ContentTypes';
78+
const mergeFile = this.addFile(mergeFileName);
79+
80+
const imports: OptionalKind<ImportDeclarationStructure>[] = [];
81+
const types: string[] = [];
82+
83+
this.project.getSourceFiles()
84+
.filter(sourceFile => sourceFile.getBaseNameWithoutExtension() !== mergeFileName)
85+
.forEach(sourceFile => forEachStructureChild(sourceFile.getStructure(),
86+
childStructure => {
87+
switch (childStructure.kind) {
88+
case StructureKind.ImportDeclaration:
89+
imports.push(childStructure);
90+
break;
91+
case StructureKind.Interface:
92+
types.push(childStructure.name);
93+
mergeFile.addInterface(childStructure);
94+
break;
95+
case StructureKind.TypeAlias:
96+
types.push(childStructure.name);
97+
mergeFile.addTypeAlias(childStructure);
98+
break;
99+
default:
100+
throw new Error(`Unhandled node type '${StructureKind[childStructure.kind]}'.`);
101+
}
102+
}));
103+
104+
// only import modules not present in merge file
105+
imports.forEach(importD => {
106+
const name = importD.moduleSpecifier.slice(2);
107+
if (!types.includes(name)) {
108+
mergeFile.addImportDeclaration(importD);
109+
}
110+
});
111+
112+
mergeFile.organizeImports({
113+
ensureNewLineAtEndOfFile: true,
114+
});
115+
116+
const fullText = mergeFile.getFullText();
117+
this.project.removeSourceFile(mergeFile);
118+
119+
return fullText;
120+
}
76121

77122
private addFile = (name: string): SourceFile => {
78123
return this.project.createSourceFile(`${name}.ts`,
@@ -86,14 +131,14 @@ export default class CFDefinitionsBuilder {
86131
return file.addInterface({name, isExported: true});
87132
};
88133

89-
private addIndexFile = (): void => {
90-
const oldIndexFile = this.project.getSourceFile(file => {
134+
private getIndexFile = (): SourceFile | undefined => {
135+
return this.project.getSourceFile(file => {
91136
return file.getBaseNameWithoutExtension() === 'index';
92137
});
138+
}
93139

94-
if (oldIndexFile) {
95-
this.project.removeSourceFile(oldIndexFile);
96-
}
140+
private addIndexFile = (): void => {
141+
this.removeIndexFile();
97142

98143
const files = this.project
99144
.getSourceFiles()
@@ -112,6 +157,13 @@ export default class CFDefinitionsBuilder {
112157
indexFile.organizeImports();
113158
};
114159

160+
private removeIndexFile = () => {
161+
const indexFile = this.getIndexFile();
162+
if (indexFile) {
163+
this.project.removeSourceFile(indexFile);
164+
}
165+
}
166+
115167
private addEntryTypeAlias = (file: SourceFile, aliasName: string, entryType: string) => {
116168
file.addTypeAlias({
117169
isExported: true,
@@ -146,48 +198,6 @@ export default class CFDefinitionsBuilder {
146198
file.addImportDeclarations(propertyImports(field, file.getBaseNameWithoutExtension()));
147199
};
148200

149-
private mergeFile = (mergeFileName = 'ContentTypes'): SourceFile => {
150-
const mergeFile = this.addFile(mergeFileName);
151-
152-
const imports: OptionalKind<ImportDeclarationStructure>[] = [];
153-
const types: string[] = [];
154-
155-
this.project.getSourceFiles()
156-
.filter(sourceFile => sourceFile.getBaseNameWithoutExtension() !== mergeFileName)
157-
.forEach(sourceFile => forEachStructureChild(sourceFile.getStructure(),
158-
childStructure => {
159-
switch (childStructure.kind) {
160-
case StructureKind.ImportDeclaration:
161-
imports.push(childStructure);
162-
break;
163-
case StructureKind.Interface:
164-
types.push(childStructure.name);
165-
mergeFile.addInterface(childStructure);
166-
break;
167-
case StructureKind.TypeAlias:
168-
types.push(childStructure.name);
169-
mergeFile.addTypeAlias(childStructure);
170-
break;
171-
default:
172-
throw new Error(`Unhandled node type '${StructureKind[childStructure.kind]}'.`);
173-
}
174-
}));
175-
176-
// only import modules not present in merge file
177-
imports.forEach(importD => {
178-
const name = importD.moduleSpecifier.slice(2);
179-
if (!types.includes(name)) {
180-
mergeFile.addImportDeclaration(importD);
181-
}
182-
});
183-
184-
mergeFile.organizeImports({
185-
ensureNewLineAtEndOfFile: true,
186-
});
187-
188-
return mergeFile;
189-
};
190-
191201
private addDefaultImports(file: SourceFile) {
192202
file.addImportDeclaration({
193203
moduleSpecifier: 'contentful',

test/cf-definitions-builder.test.ts

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -404,40 +404,40 @@ describe('A Contentful definitions builder', () => {
404404
`));
405405
});
406406

407-
it('can self-reference', async () => {
408-
builder.appendType(modelType);
409-
builder.appendType({
410-
id: 'rootId',
411-
name: 'Root Name',
412-
sys: {
413-
id: 'myType',
414-
type: 'ContentType',
415-
}, fields: [
416-
{
417-
id: 'linkFieldId',
418-
name: 'Linked entry Field',
419-
type: 'Link',
420-
localized: false,
421-
required: false,
422-
validations: [
423-
{
424-
linkContentType: [
425-
'myType',
426-
],
427-
},
428-
],
429-
disabled: false,
430-
omitted: false,
431-
linkType: 'Entry',
432-
},
433-
],
434-
});
407+
it('can self-reference', async () => {
408+
builder.appendType(modelType);
409+
builder.appendType({
410+
id: 'rootId',
411+
name: 'Root Name',
412+
sys: {
413+
id: 'myType',
414+
type: 'ContentType',
415+
}, fields: [
416+
{
417+
id: 'linkFieldId',
418+
name: 'Linked entry Field',
419+
type: 'Link',
420+
localized: false,
421+
required: false,
422+
validations: [
423+
{
424+
linkContentType: [
425+
'myType',
426+
],
427+
},
428+
],
429+
disabled: false,
430+
omitted: false,
431+
linkType: 'Entry',
432+
},
433+
],
434+
});
435435

436-
await builder.write(fixturesPath, writeFile);
436+
await builder.write(fixturesPath, writeFile);
437437

438-
const result2 = await fs.readFile(path.resolve(fixturesPath, 'TypeMyType.ts'));
438+
const result2 = await fs.readFile(path.resolve(fixturesPath, 'TypeMyType.ts'));
439439

440-
expect('\n' + result2.toString()).to.equal(stripIndent(`
440+
expect('\n' + result2.toString()).to.equal(stripIndent(`
441441
import * as Contentful from "contentful";
442442
443443
export interface TypeMyTypeFields {
@@ -446,5 +446,52 @@ describe('A Contentful definitions builder', () => {
446446
447447
export type TypeMyType = Contentful.Entry<TypeMyTypeFields>;
448448
`));
449-
});
449+
});
450+
451+
it('is not changing source project on export', async () => {
452+
builder.appendType(modelType);
453+
builder.appendType({
454+
id: 'rootId',
455+
name: 'Root Name',
456+
sys: {
457+
id: 'myType',
458+
type: 'ContentType',
459+
}, fields: [
460+
{
461+
id: 'linkFieldId',
462+
name: 'Linked entry Field',
463+
type: 'Link',
464+
localized: false,
465+
required: false,
466+
validations: [
467+
{
468+
linkContentType: [
469+
'myType',
470+
],
471+
},
472+
],
473+
disabled: false,
474+
omitted: false,
475+
linkType: 'Entry',
476+
},
477+
],
478+
});
479+
480+
let beforeWriteResult: Record<string, string> = {}
481+
let afterWriteResult: Record<string, string> = {}
482+
483+
const write = (result: Record<string, string>) => {
484+
return async (filePath: string, content: string) => {
485+
result = {[filePath]: content, ...result};
486+
}
487+
}
488+
489+
const beforeResult = builder.toString();
490+
await builder.write(fixturesPath, write(beforeWriteResult));
491+
const afterResult = builder.toString();
492+
await builder.write(fixturesPath, write(afterWriteResult));
493+
494+
expect(beforeResult).eql(afterResult);
495+
expect(beforeWriteResult).eql(afterWriteResult);
496+
});
450497
});

0 commit comments

Comments
 (0)