florian-lefebvre / astro-integration-kit

A package that contains utilities to help you build Astro integrations.
https://astro-integration-kit.netlify.app
MIT License
46 stars 8 forks source link

file factory (name pending) util and unindent util #66

Closed jdtjenkins closed 4 months ago

jdtjenkins commented 4 months ago

Just started using this to create virtual modules and dts files. This is just an idea, but would make it easier.

// file-factory.ts
import { unindent } from './unindent'

export const fileFactory = () => {
    let file = ``

    return {
        addLines(lines: string) {
            file += unindent`${ lines }`
        },
        text() {
            return file
        }
    }
}
// unindent-ts
export function unindent(strings, ...values) {
    const result = [];
    const template = strings.reduce((prev, current) => prev + values.shift() + current);

    // Find the common indentation
    const indentMatch = template.match(/^\n?(\s*)/);
    const commonIndent = indentMatch ? indentMatch[1].length : 0;

    // Remove common indentation from each line
    const lines = template.split('\n');
    for (let line of lines) {
        result.push(line.slice(commonIndent));
    }

    // Join the lines back together
    return result.join('\n');
}

Basically means you can create virtual modules and dts files much nicer:

"astro:setup:config": () => {
  const virtualModule = fileFactory()

  virtualModule.addLines(`
    export default ${ JSON.stringify({}) }
  `)
}

outputs

export default {}

all nicely formatted n stuff so you don't have to worry about indents and messy af dts files if you're trying to dynamically generate

florian-lefebvre commented 4 months ago

mmh could it be possible to take the content and format it without a super huge dependency instead (or even none)? this way users don't have to think about it, they pass their file content badly formatted and we do it for them?

BryceRussell commented 4 months ago

Misunderstanding: ~Related to this issue #65 about generating virtual module import/exports, if AIK generates import/exports it could also generate types for the module which would require a way to buffer lines/types and compile into a string for addDts~

+1 for having this functionality native to addDts or AIK in general it would make it a lot easier to generate good clean looking types (and do so dynamically)

I have a small implementation for something similar here to get an idea on how to do something like this without a package. I originally authored this as a plugin for AIK but had to move away from defineIntegration for more control over typing. This is what my API looked while I had a plugin:

"astro:config:setup": ({ createDtsBuffer }) => {

  const {
    createLineBuffer,
    addLinesToDts,
    addLinesToDtsInterface,
    addLinesToDtsNamespace,
    addLinesToDtsModule,
    writeDtsBuffer,
  } = createDtsBuffer("my-integration")

  // Add lines to .d.ts file
  addLinesToDts(`
    type ThemeName = "${themeName}";
    type ThemeConfig = NonNullable<Parameters<typeof import("${entrypoint}").default>[0]>["config"]
  `)

  // Add lines for virtual module 'my-module'
  addLinesToDtsModule(
    "my-module",
    Object.entries(objectModule)
      .map(([name, entrypoint]) => `export const "${camelCase(name)}": typeof import("${resolveImport(entrypoint)}").default;`)
  );

  // Create your own line buffer (useful for generating nested types like objects inside interfaces)
  const typeBuffer = createLineBuffer()

  typeBuffer.add(Object.entries(objectModule).map(() => ...))

  // Using the line buffer
  addLinesToDtsInterface(
    "SomeInterface",
    typeBuffer.lines
  )

  // Compile buffer into a string and write to .d.ts file
  writeDtsBuffer()

}

(This is just an example of how I solved this, not necessarily a suggestion for a full AIK API)

jdtjenkins commented 4 months ago

@florian-lefebvre Yeah I'd be up for both! There are times when you want to dynamically create dts content or virtual module content and a helper like FileFactory or the far, far superior createLineBuffer @BryceRussell mentioned is a handy way to do it.

That being said we can definitely run the content pass into addDts through unindent and then at least then the user doesn't have to worry about that for one off-files or whatever and still gets neat af files

florian-lefebvre commented 4 months ago

That one sounds interesting, idk if it has support for TS https://github.com/benjamn/recast

EDIT: it has support for it by specifying a specific parser