Repository: motion-canvas/motion-canvas
File: packages/vite-plugin/src/main.ts (TypeScript)

Code before:
}

export default ({
  project = './src/project.ts',
  output = './output',
  bufferedAssets = /\.(wav|ogg)$/,
  editor = '@motion-canvas/ui',
  proxy,
}: MotionCanvasPluginConfig = {}): Plugin => {
  const editorPath = path.dirname(require.resolve(editor));
  const editorFile = fs.readFileSync(path.resolve(editorPath, 'editor.html'));
  const htmlParts = editorFile
    .toString()
    .replace('{{style}}', `/@fs/${path.resolve(editorPath, 'style.css')}`)
    .split('{{source}}');
  const createHtml = (src: string) => htmlParts[0] + src + htmlParts[1];

  const resolvedEditorId = '\0virtual:editor';

  const timeStamps: Record<string, number> = {};
  const outputPath = path.resolve(output);
  const projects: ProjectData[] = [];
  const projectLookup: Record<string, ProjectData> = {};
  for (const url of typeof project === 'string' ? [project] : project) {
    const {name} = path.parse(url);
    const metaData = getMeta(`./src/${name}.meta`);
    const data = {name: metaData?.name ?? name, fileName: name, url};
    projects.push(data);
    projectLookup[name] = data;
  }

  let viteConfig: ResolvedConfig;

  function source(...lines: string[]) {
    return lines.join('\n');
  }

  function getMeta(metaPath: string) {
    if (fs.existsSync(metaPath)) {
      return JSON.parse(fs.readFileSync(metaPath, 'utf8'));
    }
  }

  async function createMeta(metaPath: string) {
    if (!fs.existsSync(metaPath)) {
      await fs.promises.writeFile(
        metaPath,
        JSON.stringify({version: 0}, undefined, 2),
        'utf8',
      );
    }

Review comment (suggestion):
Projects can be located anywhere in the `src` directory.
Take a look at how the project loader creates the meta path:
https://github.com/motion-canvas/motion-canvas/blob/cdd3df1bff25b04b905e289264831d8d328caaab/packages/vite-plugin/src/main.ts#L206-L207


Code after:
  name: string;
  url: string;
}

export default ({
  project = './src/project.ts',
  output = './output',
  bufferedAssets = /\.(wav|ogg)$/,
  editor = '@motion-canvas/ui',
  proxy,
}: MotionCanvasPluginConfig = {}): Plugin => {
  const plugins: PluginOptions[] = [
    {entryPoint: '@motion-canvas/core/lib/plugin/DefaultPlugin'},
  ];
  const editorPath = path.dirname(require.resolve(editor));
  const editorFile = fs.readFileSync(path.resolve(editorPath, 'editor.html'));
  const htmlParts = editorFile
    .toString()
    .replace('{{style}}', `/@fs/${path.resolve(editorPath, 'style.css')}`)
    .split('{{source}}');
  const createHtml = (src: string) => htmlParts[0] + src + htmlParts[1];
  const versions = JSON.stringify(getVersions());

  const resolvedEditorId = '\0virtual:editor';

  const timeStamps: Record<string, number> = {};
  const outputPath = path.resolve(output);
  const projects: ProjectData[] = [];
  const projectLookup: Record<string, ProjectData> = {};
  for (const url of typeof project === 'string' ? [project] : project) {
    const {name, dir} = path.posix.parse(url);
    const metaFile = `${name}.meta`;
    const metaData = getMeta(path.join(dir, metaFile));
    const data = {name: metaData?.name ?? name, fileName: name, url};
    projects.push(data);
    projectLookup[name] = data;
  }

  let viteConfig: ResolvedConfig;

  function source(...lines: string[]) {
    return lines.join('\n');
  }

  function getMeta(metaPath: string) {
    if (fs.existsSync(metaPath)) {
      return JSON.parse(fs.readFileSync(metaPath, 'utf8'));
    }
  }

  async function createMeta(metaPath: string) {