madge icon indicating copy to clipboard operation
madge copied to clipboard

Support single file component (VueJS)

Open abou7mied opened this issue 8 years ago • 36 comments

I'm using VueJS and single file components (.vue files) which have dependencies but madge shows that .vue files has no deps.

The file structure is like that

<template>
...
</template>
<style>
...
</style>
<script>
  import file from './file.js';
...
</script>

abou7mied avatar Jul 18 '17 00:07 abou7mied

Thanks for contributing!

This is an interesting case. It looks like an html parser (or a naive regex if possible) could be used to grab the imports out of inlined script tags. This could be made part of precinct for a .vue extension, but it would be nice to plug that in as a config from madge instead of precinct ballooning for all new frameworks.

This isn't a trivial change, but possible.

mrjoelkemp avatar Jul 18 '17 03:07 mrjoelkemp

Maybe you can implement an interface for extracting js code for any extension and add option like --extract-from-extension vue and madge will search for file with name extractor-vue.js

module.exports = function (contents, callback) {
  callback("text", contents.match(/something/));
  // in case of referring files like using src attribute
  callback("refer", "./referred-file.js");
}

and call the interface, maybe you can create extractors directory in the repo which have predefined extractors for popular frameworks.

I forgot to indicate in previous comment that the script tag may refer to another file using the src attribute so the return of the interface maybe text or reference

It's just an idea maybe you have a good one.

abou7mied avatar Jul 18 '17 05:07 abou7mied

I asked in Vue support about how to get script contents and they replied there are vue-template-compiler

const compiler = require('vue-template-compiler')
const output = compiler.parseComponent(`<script>import file from './file'</script>`, {pad: 'line'});

console.log("output.script", output.script);
/*
output.script { type: 'script',
  content: 'import file from \'./file\'',
  start: 8,
  attrs: {},
  end: 33 }
*/

So now we can get the contents of the script tag and src attribute if it exists.

I think make an interface for madge to handle files with custom extensions is a good idea we can make one extractor madge-extractor.js file and it should handle multiple extension

module.exports = (extension, contents, callback) => {
  let isReference = false
  if (extension === "vue") {
    const compiler = require('vue-template-compiler')
    const output = compiler.parseComponent(contents, {pad: 'line'})
    if(output.script){
      isReference = output.script.attrs.src;
      if(!isReference)
        contents = output.script.content
    }
  }

  /*  callback(isReference?, reference|contents) */
  callback(isReference, isReference ? isReference : contents);

}
madge app.js --extensions js,vue --extractor madge-extractor.js

abou7mied avatar Jul 18 '17 08:07 abou7mied

Thanks for digging into this. Can regular components have the .vue extension or only these special template files?

If only the special case applies, then the path forward is similar to how we want to add TypeScript support #64. You need the code you wrote in precinct. You may not need to modify filing-cabinet since it defaults to an es6 resolver. Although, if you're using webpack, you may or may not (on mobile, sorry I can't verify) need to support the additional .vue extension in the config for enhanced-resolve.

I'm open to reviewing PRs for the effort. A good first step is to hack precinct and maybe filing-cabinet within madge's node_modules (or npm link if you're saavy) to get a proof of concept together.

mrjoelkemp avatar Jul 21 '17 04:07 mrjoelkemp

Sounds like a good time to pick up the extension/plugin support again :)

How about something like how the webpack loaders work? Would that be suitable for precinct @mrjoelkemp? See https://webpack.js.org/concepts/loaders/

pahen avatar Aug 09 '17 11:08 pahen

That sounds good. Would madge be the supplier of the html detective and partial-resolver (like the resolvers in filing-cabinet)? Or would you pass that off to users of madge to supply their own detective/resolver combo (i.e., language pack)?

mrjoelkemp avatar Aug 09 '17 12:08 mrjoelkemp

Is it fine to have the most recently registered language pack win in the case of conflicts (i.e., when folks override an existing file extension's language pack)? Or should it get chained (like a linked list within a hashmap) on conflicts (where every registered language pack for that extension gets run on the original source?

mrjoelkemp avatar Aug 09 '17 12:08 mrjoelkemp

Just a bump here to ask about progress. Madge is a pretty exciting project, but my repo has a mix of vue and JSX files.

Christopher-Wirt avatar Dec 08 '18 03:12 Christopher-Wirt

const recursive = require('recursive-readdir');
const replace = require('replace-in-file');
const { extname } = require('path');
const { readFile, writeFile, unlink } = require('fs');
const { promisify } = require('util');

const readFileAsync = promisify(readFile);
const unlinkAsync = promisify(unlink);
const writeFileAsync = promisify(writeFile);

async function main(path = './') {
    const files = await getAllFiles(path);
    await Promise.all(files.map(file => replaceVueImportsWithJs(file)));

    const vueFiles = getVueFiles(files);
    await Promise.all(vueFiles.map(file => replaceVueFilesWithJs(file)));
}

async function getAllFiles(path) {
    return await recursive(path);
}

function getVueFiles(files) {
    return files.filter(f => f.includes('.vue'));
}

async function replaceVueImportsWithJs(file) {
    await replace({
        files: file,
        from: /\.vue/gm,
        to: '.js',
    });
}

async function replaceVueFilesWithJs(vueFile) {
    const script = await extractScript(vueFile);
    const newFileName = vueFile.replace(extname(vueFile), '.js');
    await unlinkAsync(vueFile);
    return writeFileAsync(newFileName, script, 'utf8');

    async function extractScript(file) {
        const content = await readFileAsync(file, 'utf8');
        return content.split('<script>')[1].split('</script>')[0];
    }
}

main('./src');

I just wrote a small script, which replaces all vue files and their imports with js) its a hack but solves the problem

ATTENTION

This script edits and deletes original files, i just use git to bring changes back

Enity avatar Dec 16 '18 17:12 Enity

@Enity If I could upvote you more, I def would. Thanks for your quick and dirty script =) It helped me handoff a mess of a Vue app to another Dev. Cheers!

Also, love this project. It's incredible!

Mk-Etlinger avatar Mar 07 '19 22:03 Mk-Etlinger

any news?

maxim-usikov avatar Jul 30 '19 17:07 maxim-usikov

I create a polyfill for vue project.

gist: https://gist.github.com/songlairui/287eaac29cda65cd5f1f7471b6661503 demo: https://github.com/songlairui/demo-madge-vue

image

songlairui avatar Mar 05 '20 13:03 songlairui

FYI, Nuxt.js just released the Components Module which I anticipate will grow in popularity. Whoever takes up this issue should keep that in mind. @Atinux maybe you know someone in the Vue community who would be interested in getting Madge and Vue to play together :)

grokpot avatar May 25 '20 08:05 grokpot

any news?

bailnl avatar Nov 20 '20 08:11 bailnl

Three years passed, has any progress...?

reuwi avatar Dec 20 '20 05:12 reuwi

@reuwi please feel free to submit a PR.

mrjoelkemp avatar Dec 20 '20 13:12 mrjoelkemp

I create a polyfill for vue project.

gist: https://gist.github.com/songlairui/287eaac29cda65cd5f1f7471b6661503 demo: https://github.com/songlairui/demo-madge-vue

image

I think it's a good way then @enity's method, when I use madge 4.0.2 , songlairui's polyfill can't work,it confuse me one day.

finally,I found madge add precinct to dependencies since 3.9.x,but not use it ,the reason is when dependency-tree require(precinct) and then polyfill require(precinct) ,these are two 'instance' of precinct.

I had to use const precinct = require('./node_modules/dependency-tree/node_modules/precinct') to keep this ployfill working.

By the way,Madge is great tools for check project's file relations, and why does madge add precinct dependence, madge dependence dependency-tree has depend on precinct yet? @pahen @mrjoelkemp

Thanks a lot!

airene avatar Jun 09 '21 07:06 airene

I needed this feature and implemented the support for Vue, I can create PRs soon

Havunen avatar Feb 18 '23 12:02 Havunen

I created a new detective vue package to handle vue single file components https://github.com/Havunen/detective-vue2

Created PR to node-precinct https://github.com/dependents/node-precinct/pull/112

Support for vue single file components to node-filing-cabinet https://github.com/dependents/node-filing-cabinet/pull/111

Then new version of precinct and node-filing-cabinet needs to be published and possibly bump the dependencies here: https://github.com/dependents/node-dependency-tree

Then finally PR could be created to add support to Madge

Havunen avatar Feb 18 '23 20:02 Havunen

@mrjoelkemp can you review the PRs please, it would be nice for madge to support vue files 😀

Havunen avatar Feb 23 '23 16:02 Havunen

This is way out of scope, but a tree-sitter parser would be more robust and able to handle all of kinds of alternate syntaxes, such as vue, svelte, tsx and non-js files such as html, pug, postcss and mixed variants such as svelte-pug, vue-scss-typescript.

mikeslattery avatar May 02 '23 13:05 mikeslattery

Any news? This addition will be really useful. I'm running madge 6.1.0 Thanks! mario

crystalfp avatar Sep 13 '23 03:09 crystalfp

Can you please merge the PRs? @pahen @XhmikosR

Havunen avatar Sep 14 '23 16:09 Havunen

Is this still active?

frankykubo avatar Nov 28 '23 13:11 frankykubo

Madge has dependencies to these packages which have pending PRs https://github.com/dependents/node-filing-cabinet/pull/111 https://github.com/dependents/node-precinct/pull/124

Havunen avatar Nov 28 '23 13:11 Havunen

For vue3, Enity's script needed some changes due to typescript support such as

freemedom avatar Dec 08 '23 05:12 freemedom

I create a polyfill for vue project.

gist: https://gist.github.com/songlairui/287eaac29cda65cd5f1f7471b6661503 demo: https://github.com/songlairui/demo-madge-vue

image

thats not work for me , I change 2

  1. change filing-cabinet,use tsLookup to handle .vue file image
  2. use decoratePrecinct to change precinct.paperwork

only use decoratePrecient image

require it image

result: thats work

image

xiaolee55 avatar Jan 22 '24 08:01 xiaolee55

I create a polyfill for vue project. gist: https://gist.github.com/songlairui/287eaac29cda65cd5f1f7471b6661503 demo: https://github.com/songlairui/demo-madge-vue image

thats not work for me , I change 2

  1. change filing-cabinet,use tsLookup to handle .vue file

image 2. use decoratePrecinct to change precinct.paperwork only use decoratePrecient image

require it image

result: thats work

image

就得是这个啊,靓仔,非常希望这个能力可以支持进去,因为当前vue已经非常热门了。

calandnong avatar Mar 18 '24 11:03 calandnong

@pahen 麻烦看看这个哈

calandnong avatar Mar 18 '24 11:03 calandnong

Just ran into this case, would really appreciate introduce .vue support. Is this still active?

kqhasaki avatar Apr 03 '24 07:04 kqhasaki