Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@
"meow": "^9.0.0"
},
"peerDependencies": {
"prettier": ">= 1"
"prettier": ">= 1",
"ts-node": ">= 9.0.0"
},
"peerDependenciesMeta": {
"ts-node": {
"optional": true
}
},
"devDependencies": {
"@contentful/rich-text-types": "^13.4.0",
Expand All @@ -59,6 +65,7 @@
"rollup-plugin-typescript2": "^0.22.1",
"semantic-release": "^17.4.1",
"ts-jest": "^26.0.0",
"ts-node": "^10.4.0",
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.18.0",
"tslint-config-standard": "^8.0.1",
Expand Down
9 changes: 4 additions & 5 deletions src/contentful-typescript-codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import render from "./renderers/render"
import renderFieldsOnly from "./renderers/renderFieldsOnly"
import path from "path"
import { outputFileSync } from "fs-extra"
import { loadEnvironment } from "./loadEnvironment"

const meow = require("meow")

export { ContentfulEnvironment, EnvironmentGetter } from "./loadEnvironment"

const cli = meow(
`
Usage
Expand Down Expand Up @@ -60,11 +63,7 @@ const cli = meow(
)

async function runCodegen(outputFile: string) {
const getEnvironmentPath = path.resolve(process.cwd(), "./getContentfulEnvironment.js")
const getEnvironment = require(getEnvironmentPath)
const environment = await getEnvironment()
const contentTypes = await environment.getContentTypes({ limit: 1000 })
const locales = await environment.getLocales()
const { contentTypes, locales } = await loadEnvironment()
const outputPath = path.resolve(process.cwd(), outputFile)

let output
Expand Down
77 changes: 77 additions & 0 deletions src/loadEnvironment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as path from "path"
import * as fs from "fs"
import { ContentfulCollection, ContentTypeCollection, LocaleCollection } from "contentful"

/* istanbul ignore next */
const interopRequireDefault = (obj: any): { default: any } =>
obj && obj.__esModule ? obj : { default: obj }

type Registerer = { enabled(value: boolean): void }

let registerer: Registerer | null = null

function enableTSNodeRegisterer() {
if (registerer) {
registerer.enabled(true)

return
}

try {
registerer = require("ts-node").register() as Registerer
registerer.enabled(true)
} catch (e) {
if (e.code === "MODULE_NOT_FOUND") {
throw new Error(
`'ts-node' is required for TypeScript configuration files. Make sure it is installed\nError: ${e.message}`,
)
}

throw e
}
}

function determineEnvironmentPath() {
const pathWithoutExtension = path.resolve(process.cwd(), "./getContentfulEnvironment")

if (fs.existsSync(`${pathWithoutExtension}.ts`)) {
return `${pathWithoutExtension}.ts`
}

return `${pathWithoutExtension}.js`
}

export interface ContentfulEnvironment {
getContentTypes(options: { limit: number }): Promise<ContentfulCollection<unknown>>
getLocales(): Promise<ContentfulCollection<unknown>>
}

export type EnvironmentGetter = () => Promise<ContentfulEnvironment>

function getEnvironmentGetter(): EnvironmentGetter {
const getEnvironmentPath = determineEnvironmentPath()

if (getEnvironmentPath.endsWith(".ts")) {
enableTSNodeRegisterer()

return interopRequireDefault(require(getEnvironmentPath)).default
}

return require(getEnvironmentPath)
}

export async function loadEnvironment() {
try {
const getEnvironment = getEnvironmentGetter()
const environment = await getEnvironment()

return {
contentTypes: (await environment.getContentTypes({ limit: 1000 })) as ContentTypeCollection,
locales: (await environment.getLocales()) as LocaleCollection,
}
} finally {
if (registerer) {
registerer.enabled(false)
}
}
}
104 changes: 104 additions & 0 deletions test/loadEnvironment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as fs from "fs"
import { loadEnvironment } from "../src/loadEnvironment"

const getContentfulEnvironmentFileFactory = jest.fn((_type: string) => {
return () => ({
getContentTypes: () => [],
getLocales: () => [],
})
})

jest.mock(
require("path").resolve(process.cwd(), "./getContentfulEnvironment.js"),
() => getContentfulEnvironmentFileFactory("js"),
{ virtual: true },
)

jest.mock(
require("path").resolve(process.cwd(), "./getContentfulEnvironment.ts"),
() => getContentfulEnvironmentFileFactory("ts"),
{ virtual: true },
)

const tsNodeRegistererEnabled = jest.fn()
const tsNodeRegister = jest.fn()

jest.mock("ts-node", () => ({ register: tsNodeRegister }))

describe("loadEnvironment", () => {
beforeEach(() => {
jest.resetAllMocks()
jest.restoreAllMocks()

getContentfulEnvironmentFileFactory.mockReturnValue(() => ({
getContentTypes: () => [],
getLocales: () => [],
}))
tsNodeRegister.mockReturnValue({ enabled: tsNodeRegistererEnabled })
})

describe("when getContentfulEnvironment.ts exists", () => {
beforeEach(() => {
jest.spyOn(fs, "existsSync").mockReturnValue(true)
})

describe("when ts-node is not found", () => {
beforeEach(() => {
// technically this is throwing after the `require` call,
// but it still tests the same code path so is fine
tsNodeRegister.mockImplementation(() => {
throw new (class extends Error {
public code: string

constructor(message?: string) {
super(message)
this.code = "MODULE_NOT_FOUND"
}
})()
})
})

it("throws a nice error", async () => {
await expect(loadEnvironment()).rejects.toThrow(
"'ts-node' is required for TypeScript configuration files",
)
})
})

describe("when there is another error", () => {
beforeEach(() => {
tsNodeRegister.mockImplementation(() => {
throw new Error("something else went wrong!")
})
})

it("re-throws", async () => {
await expect(loadEnvironment()).rejects.toThrow("something else went wrong!")
})
})

describe("when called multiple times", () => {
it("re-uses the registerer", async () => {
await loadEnvironment()
await loadEnvironment()

expect(tsNodeRegister).toHaveBeenCalledTimes(1)
})
})

it("disables the registerer afterwards", async () => {
await loadEnvironment()

expect(tsNodeRegistererEnabled).toHaveBeenCalledWith(false)
})
})

it("requires the javascript config", async () => {
jest.spyOn(fs, "existsSync").mockReturnValue(false)

await loadEnvironment()

expect(getContentfulEnvironmentFileFactory).toHaveBeenCalledWith("js")
expect(getContentfulEnvironmentFileFactory).not.toHaveBeenCalledWith("ts")
})
})
77 changes: 76 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,18 @@
resolved "https://registry.yarnpkg.com/@contentful/rich-text-types/-/rich-text-types-13.4.0.tgz#a59c311ebd1b801ee00edbc08663c8d78da26171"
integrity sha512-YPdYqGmWiGAood7ri2BUXfPQKNthkQYV1rmQdaq4UAxWK5NLB5NXckaUYLbohqhHKtrq4tnfzVn+ePWID7Dzbg==

"@cspotcode/[email protected]":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==

"@cspotcode/[email protected]":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"

"@iarna/cli@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641"
Expand Down Expand Up @@ -759,6 +771,26 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==

"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==

"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==

"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==

"@tsconfig/node16@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==

"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
version "7.1.12"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
Expand Down Expand Up @@ -960,6 +992,11 @@ acorn-walk@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==

acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==

acorn@^7.1.0, acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
Expand All @@ -970,6 +1007,11 @@ acorn@^8.0.5:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe"
integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==

acorn@^8.4.1:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==

agent-base@4, agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
Expand Down Expand Up @@ -1135,6 +1177,11 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"

arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==

argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
Expand Down Expand Up @@ -2052,6 +2099,11 @@ create-error-class@^3.0.0:
dependencies:
capture-stack-trace "^1.0.0"

create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==

cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
Expand Down Expand Up @@ -5073,7 +5125,7 @@ make-dir@^3.0.0:
dependencies:
semver "^6.0.0"

[email protected]:
[email protected], make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
Expand Down Expand Up @@ -7725,6 +7777,24 @@ ts-jest@^26.0.0:
semver "7.x"
yargs-parser "20.x"

ts-node@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7"
integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==
dependencies:
"@cspotcode/source-map-support" "0.7.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
"@tsconfig/node16" "^1.0.2"
acorn "^8.4.1"
acorn-walk "^8.1.1"
arg "^4.1.0"
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
yn "3.1.1"

[email protected]:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
Expand Down Expand Up @@ -8406,3 +8476,8 @@ yargs@^8.0.2:
which-module "^2.0.0"
y18n "^3.2.1"
yargs-parser "^7.0.0"

[email protected]:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==