Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get a reference to the importing file's path in Custom Components #2344

Closed
WilliamABradley opened this issue Aug 19, 2021 · 4 comments
Closed
Labels

Comments

@WilliamABradley
Copy link

WilliamABradley commented Aug 19, 2021

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
I want to build a sass rendering component, that you provide it a .scss path, and it wraps the style internals.

Component:

import { HeadComponent } from 'mjml-core';
import fs from 'fs';
import path from 'path';
import sass from 'sass';

export default class MjScss extends HeadComponent {
  static componentName = 'mj-scss';
  static endingTag = true;
  static dependencies = {
    'mj-head': ['mj-scss'],
  };
  static allowedAttributes = {
    src: 'string',
    inline: 'string',
  };

  handler() {
    const { add } = this.context;
    // We have no idea where this Component is called from,
    // therefore we assume there is a scss folder 2 directories away
    // Which assumes this component is in a folder, contained by a components folder.
    const callerPath = `${__dirname}/../../scss`;
    const filePath: string = path.resolve(callerPath, this.getAttribute('src'));
    if (!fs.existsSync(filePath)) {
      throw new Error(`Can't find scss file at ${filePath}`);
    }

    const scss = sass.renderSync({
      file: filePath,
    }).css.toString();

    add(
      this.getAttribute('inline') === 'inline' ? 'inlineStyle' : 'style',
      scss
    );
  }
}

Usage:

<mj-scss src="theme.scss"></mj-scss>

Describe the solution you'd like
A clear and concise description of what you want to happen.

Component:

import { HeadComponent } from 'mjml-core';
import fs from 'fs';
import path from 'path';
import sass from 'sass';

export default class MjScss extends HeadComponent {
  static componentName = 'mj-scss';
  static endingTag = true;
  static dependencies = {
    'mj-head': ['mj-scss'],
  };
  static allowedAttributes = {
    src: 'string',
    inline: 'string',
  };

  handler() {
    const { add, callerPath } = this.context;
    const filePath: string = path.resolve(callerPath, this.getAttribute('src'));
    if (!fs.existsSync(filePath)) {
      throw new Error(`Can't find scss file at ${filePath}`);
    }

    const scss = sass.renderSync({
      file: filePath,
    }).css.toString();

    add(
      this.getAttribute('inline') === 'inline' ? 'inlineStyle' : 'style',
      scss
    );
  }
}

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

^ implemented a known folder approach above.

Additional context
Add any other context or screenshots about the feature request here.

@iRyusa
Copy link
Member

iRyusa commented Aug 27, 2021

It shouldn't be that hard to pass down the filePath in globalData https://github.com/mjmlio/mjml/blob/master/packages/mjml-core/src/index.js#L140 feel free to open a pr on this one

@kmcb777
Copy link
Collaborator

kmcb777 commented Jan 13, 2022

Just opened a PR for this : #2407
@WilliamABradley with this you'll be able to get the path of the mjml file in which the component is called, that's what you needed right ? If it's in a partial included with mj-include, it will give the path of the partial.
In the component it will be provided in this.props.absoluteFilePath

@WilliamABradley
Copy link
Author

@kmcb777 This works a treat! Just updated my component to use it.
It is unfortunate that @types/mjml npm package is out of date though 😢

@WilliamABradley
Copy link
Author

For future reference of anyone interested, the implementation is now:

import { HeadComponent } from 'mjml-core';
import fs from 'fs';
import path from 'path';
import sass from 'sass';
import { AttributesFromProps, registerMJMLComponent } from '..';

export type MjScssProps = {
  src: string;
  inline?: string;
};

export default class MjScss extends HeadComponent {
  static componentName = 'mj-scss';
  static endingTag = true;
  static dependencies = {
    'mj-head': ['mj-scss'],
  };
  static allowedAttributes: AttributesFromProps<MjScssProps> = {
    src: 'string',
    inline: 'string',
  };

  handler() {
    const { add } = this.context;
    // @types/mjml is not up to date.
    const absoluteFilePath: string = (this.props as any).absoluteFilePath;

    // Get directory of file, if the path is a file.
    const callerPath = fs.lstatSync(absoluteFilePath).isDirectory() ? absoluteFilePath : path.dirname(absoluteFilePath);
    const filePath: string = path.resolve(callerPath, this.getAttribute('src'));
    if (!fs.existsSync(filePath)) {
      throw new Error(`Can't find scss file at ${filePath} (From ${absoluteFilePath})`);
    }

    const scss = sass
      .renderSync({
        file: filePath,
        includePaths: [callerPath],
      })
      .css.toString();

    add(this.getAttribute('inline') === 'inline' ? 'inlineStyle' : 'style', scss);
  }
}

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

No branches or pull requests

3 participants