Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

glimmer-babel preset failing for typescript files #365

Open
lifeart opened this issue Oct 11, 2021 · 10 comments
Open

glimmer-babel preset failing for typescript files #365

lifeart opened this issue Oct 11, 2021 · 10 comments

Comments

@lifeart
Copy link
Contributor

lifeart commented Oct 11, 2021

given:

import Component, { hbs } from '@glimmerx/component';

import HelloWorld from './components/HelloWorld.hbs';

export default class App extends Component {
    static template = hbs`
        <HelloWorld />
   `
}

error:

image

once I'm console.log(HelloWorld), everything works just fine, looks like plugin should count .hbs, .gbs extensions

given:

import Component, { hbs } from '@glimmerx/component';

import HelloWorld from './components/HelloWorld.hbs';
console.log(HelloWorld);
export default class App extends Component {
    static template = hbs`
        <HelloWorld />
   `
}
@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

workaround before transform to get it working

 const imports = parseStaticImports(code).filter(e => {
        return e.moduleName.startsWith('@glimmerx/modifier') || !e.moduleName.startsWith("@");
      }).map((el) => [...el.namedImports.map(e => e.alias), el.defaultImport]).reduce((acc, items) => {
        return acc.concat(items);
      }, []);
      
      code = `
        ${code};
        //
        [${imports.map(e => `${e}`).join(',')}];
      `;

@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

problem in this function: https://github.com/ember-cli/babel-plugin-htmlbars-inline-precompile/blob/master/index.js#L142

because imported template is not binded to the scope

// this implementation of getScope solves missing scope lookup

  function getScope(scope) {
    let names = Object.keys(scope.references);

    while (scope) {
      
      for (let binding in scope.bindings) {
        names.push(binding);
      }

      if (!scope.parent) {
        Object.keys(scope.references).forEach((ref) => {
          if (!names.includes(ref)) {
            names.push(ref);
          }
        });
      }

      scope = scope.parent;
    }

    return names;
  }

but not solve final problem

@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

diff --git a/node_modules/babel-plugin-htmlbars-inline-precompile/index.js b/node_modules/babel-plugin-htmlbars-inline-precompile/index.js
index 700b3d6..29d9316 100644
--- a/node_modules/babel-plugin-htmlbars-inline-precompile/index.js
+++ b/node_modules/babel-plugin-htmlbars-inline-precompile/index.js
@@ -143,6 +143,14 @@ module.exports = function (babel) {
         names.push(binding);
       }
 
+      if (!scope.parent) {
+        Object.keys(scope.references).forEach((ref) => {
+          if (!names.includes(ref)) {
+            names.push(ref);
+          }
+        });
+      }
+
       scope = scope.parent;
     }
 

@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

next issue we have:

on modifier import is removed after compilation, but scope referencing to it

input:

import Component, { hbs } from '@glimmerx/component';
import { on, action } from '@glimmerx/modifier';
export default class App extends Component {
    updateValue() { console.log(1) }
    static template = hbs`
        <input {{on 'input' this.updateValue}} />
    `;
}

compiled output:

import { setComponentTemplate as _setComponentTemplate } from "@glimmer/core";  
import { createTemplateFactory as _createTemplateFactory } from "@glimmer/core";
import Component from '@glimmerx/component';
export default class App extends Component {
  updateValue() {
    console.log(1);
  }

}

_setComponentTemplate(_createTemplateFactory(
/*

        <input {{on 'input' this.updateValue}} />

*/
{
  "id": "HHfcxqIY",
  "block": "[[[1,\"\\n        \"],[11,\"input\"],[4,[32,0],[\"input\",[30,0,[\"updateValue\"]]],null],[12],[13],[1,\"\\n    \"]],[],false,[]]",        
  "moduleName": "(unknown template module)",
  "scope": () => [on],
  "isStrictMode": true
}), App);

if I import { action, on } from @glimmerx/modifier;

I have only { action } after template compilation:

image

If I replace @glimmerx with @glimmer:

image

@lifeart
Copy link
Contributor Author

lifeart commented Oct 11, 2021

looks like issue in @babel/preset-typescript, it's removing unused imports, may be related issue: babel/babel#9723
// https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAMwhRUIjgciRTBuAKAIFMAPSWOAE2IQEMBXAG3gGMm6BnTuAMWQBGdKHADeAXyA

possible workaround: not use ts, or parse TS -> apply hbs transform -> print ts,

@josemarluedke
Copy link
Contributor

I believe that the TS removing the imports also prevents template imports to work in ember with TS. So, fixing support for TS would be the best approach instead of not having TS support at all.

@lifeart
Copy link
Contributor Author

lifeart commented Oct 12, 2021

@josemarluedke I have one workaround for vite, collecting all imports before ast transform and appending all imports to array, to prevent ts deleting it.

import { transformSync } from "@babel/core";

import babelGlimmerPreset from "@glimmerx/babel-preset";
import tsPreset from "@babel/preset-typescript";
import parseStaticImports from "parse-static-imports";

const templateFileRegex = /\.(hbs)$/;
const fixDelimiter = '// [will-be-removed]';

export default function vitePluginBabelImport(
  plgOptions
) {
  let viteConfig;
  return {
    name: 'vite:glimmerx',
    enforce: 'pre',
    configResolved(resolvedConfig) {
      viteConfig = resolvedConfig;
    },

    transform(rawCode, id) {
      let code = rawCode;
      if (templateFileRegex.test(id)) {
        code = `
          import { hbs } from '@glimmerx/component';
          export default hbs\`${rawCode.trim()}\`;
        `.trim();
      } else if (!id.endsWith('.ts') && !id.endsWith('.js')) {
        return;
      }
      
      const imports = parseStaticImports(code).filter(e => {
        return e.moduleName.startsWith('@glimmerx/') || !e.moduleName.startsWith("@");
      }).map((el) => [...el.namedImports.map(e => e.alias), el.defaultImport]).reduce((acc, items) => {
        return acc.concat(items);
      }, []);
      
      code = `
        ${code};
        ${fixDelimiter}
        [${imports.map(e => `${e}`).join(',')}];
      `;

      const result = transformSrcCode(code, id, plgOptions, viteConfig);

      return {
        code: result.split(fixDelimiter)[0].trim(),
        map: null,
      };
    },
  };
}


function transformSrcCode(code, fileName, plgOptions, viteConfig) {
    let result = transformSync(code, {
        sourceType: "module",
        babelrc: false,
        configFile: false,
        envName: viteConfig.mode,
        filename: fileName,
        presets: [tsPreset, function(api, opts) {
            return babelGlimmerPreset(api, {...opts, ...{
                isDebug: !viteConfig.isProduction
            }})
        }]
    });
    return result.code;
}

@lifeart lifeart changed the title glimmer-babel preset failing with component import with extensions glimmer-babel preset failing for typescript files Oct 17, 2021
@knownasilya
Copy link

What needs resolving to make this preset work for TS?

@lifeart
Copy link
Contributor Author

lifeart commented Oct 2, 2022

@knownasilya with latest TS compiler, new flag introduced, to not delete "not used" imports. It should help (but i'm did not tryied)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants