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.
BBL Node.js