Bonus
J'ai ajouté quelques loaders bonus histoire de vous montrer ce qu'il est possible de faire avec les loaders:
Loader CommonJs & ESM GraphQL
Exemple de loader qui fonctionne à la fois avec CommonJs et ESM.
const {readFileSync} = require('fs')
const VALID_EXTENSIONS = ['graphql', 'graphqls', 'gql', 'gqls']
function handleModule (m, filename) {
m.exports = readFileSync(filename, 'utf-8')
}
VALID_EXTENSIONS.forEach((ext) => {
require.extensions[`.${ext}`] = handleModule
})
import {register} from 'node:module'
import {pathToFileURL} from 'node:url'
register(pathToFileURL(`${import.meta.dirname}/graphql.hook.js`))
const extensionsRegex = /\.(graphql|gql|graphqls|gqls)$/
export async function load (url, context, nextLoad) {
if (extensionsRegex.test(url)) {
// Load the raw source code.
const {source: rawSource} = await nextLoad(url, {...context, format: 'module'})
// Then transform it into a JavaScript module.
const transformedSource = `export default \`${rawSource.toString()}\``
return {
format: 'module',
shortCircuit: true,
source: transformedSource
}
}
// Let Node.js handle all other URLs.
return nextLoad(url)
}
L'astuce est d'utiliser le package.json
pour définir quoi utiliser en fonction de l'environnement :
{
"name": "@bbl/graphql-loader",
"version": "1.0.0",
"description": "GraphQL loader for Node.js",
"exports": {
"./register": {
"import": "./esm/register.js",
"default": "./cjs/register.js"
}
}
}
La version GraphQL avancée
Ce loader parse les fichiers .graphql
en utilisant cette fois la library graphql
. Il transforme ensuite le code et exporte les fonctions nommées. En complément, il génère le typage des fonctions pour TypeScript.
import {parse} from 'graphql'
import fs from 'node:fs/promises'
import {basename, dirname, join} from 'node:path'
const extensionsRegex = /\.(graphql|gql|graphqls|gqls)$/
/**
* Emit typings file to allow importing graphql in a typescript project
* @param url
* @param typings
*/
function emitTypings (url, typings) {
const filename = basename(url)
const dir = dirname(url)
const dest = join(dir, filename.replace(extensionsRegex, '.d.ts')).split('file:')[1]
const content = `
declare module '*/${filename}' {
${typings}
}`
return fs.writeFile(dest, content, {encoding: 'utf-8'})
}
function transformSource (parsedSource) {
const {exports, typings} = parsedSource.definitions.reduce(({exports, typings}, definition) => {
const def = `export const ${definition.name.value}`
return {
exports: exports.concat(`${def} = \`${definition.name.loc.source.body}\``),
typings: typings.concat(`${def}: string`)
}
}, {
typings: [],
exports: []
})
return {
typings: typings.join('\n\t'),
exports: exports.join('\n')
}
}
export async function load (url, context, nextLoad) {
if (extensionsRegex.test(url)) {
const {source: rawSource} = await nextLoad(url, {...context, format: 'module'})
// 1 - Parse graphql source code using graphql parser
const parsedSource = parse(rawSource.toString())
// 2 - Transform graphql source code into javascript source code and returns typings also
const {typings, exports} = transformSource(parsedSource)
// 3 - Emit typings file on the fly
emitTypings(url, typings).catch((er) => console.warn(er))
// 4 - Return transformed source code
return {
format: 'module',
shortCircuit: true,
source: exports
}
}
// Let Node.js handle all other URLs.
return nextLoad(url)
}
import {register} from 'node:module'
import {pathToFileURL} from 'node:url'
register(pathToFileURL(`${import.meta.dirname}/graphql.hook.js`))
Loader SWC
Ce loader permet de transformer le code TypeScript en JavaScript en utilisant SWC.
import {register} from 'node:module'
register('@swc-node/register/esm', {
parentURL: import.meta.url
})
Note
Actuellement, SWC support la version (Résolue : https://github.com/swc-project/swc-node/pull/748/files)--loader
mais pas encore la version --import
.
Différence entre --loader
et --import
?
Le flag --import
impose le passage par la function register
de node:module
. Là où le flag --loader
utilise directement le fichier avec nos hooks. Cependant, cette version ne permet pas le chainage de loader.