Search code examples
symfonytypescriptassetic

How do I specify a tsconfig.json for assetic's TypeScript filter?


I'm using Symfony 3.3 and the assetic bundle. I have this in my config.yml:

assetic:
    debug:          '%kernel.debug%'
    use_controller: '%kernel.debug%'
    filters:
        cssrewrite: ~
        typescript: 
            bin: "%kernel.root_dir%/../node_modules/typescript/bin/tsc"
            apply_to: "\\.ts$"

When I try to compile my TypeScript files, I get this error:

Output:
../../../../private/var/folders/94/45yn02792ksfglm12pjxxlp00000gn/T/assetic_typescript_in59bccf08e6357/my-form.ts.ts(6,1): error TS6131: Cannot compile modules using option 'out' unless the '--module' flag is 'amd' or 'system'.
../../../../private/var/folders/94/45yn02792ksfglm12pjxxlp00000gn/T/assetic_typescript_in59bccf08e6357/my-form.ts.ts(6,22): error TS2307: Cannot find module './my-obj.model'.

This occurs after I use an import statements like import { Obj } from './my-obj.model';. If I don't use import statements, ts compilation works fine.

How do I add give assetic's TypeScript filter either a --module system parameter or a tsconfig.json?


Solution

  • error TS6131: Cannot compile modules using option 'out' unless the '--module' flag is 'amd' or 'system'.

    Extra options for tsc command through AsseticBundle is not possible at the moment.

    error TS2307: Cannot find module './model'.

    and here AsseticBundle save each individual ts file into a tmp dir/file to compile it, so it doesn't support this feature either.

    The good news is that you can override the behavior of the assetic.filter.typescript service to fix it. Let's add this class to the application:

    namespace AppBundle\Assetic\Filter;
    
    use Assetic\Asset\AssetInterface;
    use Assetic\Exception\FilterException;
    use Assetic\Util\FilesystemUtils;
    
    class TypeScriptFilter extends \Assetic\Filter\TypeScriptFilter
    {
        private $tscBin;
        private $nodeBin;
    
        public function __construct($tscBin = '/usr/bin/tsc', $nodeBin = null)
        {
            $this->tscBin = $tscBin;
            $this->nodeBin = $nodeBin;
        }
    
        public function filterLoad(AssetInterface $asset)
        {
            $pb = $this->createProcessBuilder($this->nodeBin
                ? array($this->nodeBin, $this->tscBin)
                : array($this->tscBin));
    
            // using source path to compile and allow "imports".
            $inputPath = $asset->getSourceRoot().DIRECTORY_SEPARATOR.$asset->getSourcePath();
            $outputPath = FilesystemUtils::createTemporaryFile('typescript_out');
    
            $pb
                ->add($inputPath)
                // passing the required options here
                ->add('--module')->add('system')
                ->add('--out')
                ->add($outputPath)
            ;
    
            $proc = $pb->getProcess();
            $code = $proc->run();
    
            if (0 !== $code) {
                if (file_exists($outputPath)) {
                    unlink($outputPath);
                }
                throw FilterException::fromProcess($proc)->setInput($asset->getContent());
            }
    
            if (!file_exists($outputPath)) {
                throw new \RuntimeException('Error creating output file.');
            }
    
            $compiledJs = file_get_contents($outputPath);
            unlink($outputPath);
    
            $asset->setContent($compiledJs);
        }
    }
    

    Then, change the value of the parameter class to override the original service:

    # app/config/services.yml
    parameters:
        assetic.filter.typescript.class: 'AppBundle\Assetic\Filter\TypeScriptFilter'
    

    It worked for me with your assetic configuration.


    In the other hand, the new recommended way to manage assets for Symfony applications: Webpack Encore giving you a clean & powerful API for bundling JavaScript modules, pre-processing CSS & JS and compiling and minifying assets, with support for TypeScript loader.