Skip to content

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.

js
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
})
js
import {register} from 'node:module'
import {pathToFileURL} from 'node:url'

register(pathToFileURL(`${import.meta.dirname}/graphql.hook.js`))
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 :

json
{
  "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.

js
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)
}
js
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.

js
import {register} from 'node:module'

register('@swc-node/register/esm', {
  parentURL: import.meta.url
})

Note

Actuellement, SWC support la version --loader mais pas encore la version --import. (Résolue : https://github.com/swc-project/swc-node/pull/748/files)

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.