tonaljs / tonal

A functional music theory library for Javascript
https://tonaljs.github.io/tonal/docs
3.74k stars 210 forks source link

Importing Mode from mode module call back error in browser environment #387

Open AppleIsCute opened 11 months ago

AppleIsCute commented 11 months ago

Hi, I'm trying to write a music web with this library. I'm a rookie developer so I'm not sure if the following error is my own problem. I'm trying to import the mode module into my web app so I write this in my html:

<script type="module"> import { Mode } from "./node_modules/@tonaljs/mode/dist/index.js";</script> (node_modules is a file generated while downloading the tonaljs package and all the modules are in it )

And I got error from my console:

Uncaught SyntaxError: The requested module './node_modules/@tonaljs/mode/dist/index.js' does not provide an export named 'default'

For further info, the following is the index.ts. while running the web on localhost:

import { rotate } from "@tonaljs/collection";
import { deprecate, Named, NoteName, transpose } from "@tonaljs/core";
import { simplify, transposeFifths } from "@tonaljs/interval";
import { EmptyPcset, Pcset } from "@tonaljs/pcset";
import { get as getType } from "@tonaljs/scale-type";

const MODES = [
  [0, 2773, 0, "ionian", "", "Maj7", "major"],
  [1, 2902, 2, "dorian", "m", "m7"],
  [2, 3418, 4, "phrygian", "m", "m7"],
  [3, 2741, -1, "lydian", "", "Maj7"],
  [4, 2774, 1, "mixolydian", "", "7"],
  [5, 2906, 3, "aeolian", "m", "m7", "minor"],
  [6, 3434, 5, "locrian", "dim", "m7b5"],
] as const;

type ModeDatum = typeof MODES[number];

export interface Mode extends Pcset {
  readonly name: string;
  readonly modeNum: number;
  readonly alt: number; // number of alterations === number of fiths
  readonly triad: string;
  readonly seventh: string;
  readonly aliases: string[];
}

const NoMode: Mode = {
  ...EmptyPcset,
  name: "",
  alt: 0,
  modeNum: NaN,
  triad: "",
  seventh: "",
  aliases: [],
};

const modes: Mode[] = MODES.map(toMode);
const index: Record<string, Mode> = {};
modes.forEach((mode) => {
  index[mode.name] = mode;
  mode.aliases.forEach((alias) => {
    index[alias] = mode;
  });
});

type ModeLiteral = string | Named;

/**
 * Get a Mode by it's name
 *
 * @example
 * get('dorian')
 * // =>
 * // {
 * //   intervals: [ '1P', '2M', '3m', '4P', '5P', '6M', '7m' ],
 * //   modeNum: 1,
 * //   chroma: '101101010110',
 * //   normalized: '101101010110',
 * //   name: 'dorian',
 * //   setNum: 2902,
 * //   alt: 2,
 * //   triad: 'm',
 * //   seventh: 'm7',
 * //   aliases: []
 * // }
 */
export function get(name: ModeLiteral): Mode {
  return typeof name === "string"
    ? index[name.toLowerCase()] || NoMode
    : name && name.name
    ? get(name.name)
    : NoMode;
}

export const mode = deprecate("Mode.mode", "Mode.get", get);

/**
 * Get a list of all modes
 */
export function all() {
  return modes.slice();
}
export const entries = deprecate("Mode.mode", "Mode.all", all);

/**
 * Get a list of all mode names
 */
export function names() {
  return modes.map((mode) => mode.name);
}

function toMode(mode: ModeDatum): Mode {
  const [modeNum, setNum, alt, name, triad, seventh, alias] = mode;
  const aliases = alias ? [alias] : [];
  const chroma = Number(setNum).toString(2);
  const intervals = getType(name).intervals;
  return {
    empty: false,
    intervals,
    modeNum,
    chroma,
    normalized: chroma,
    name,
    setNum,
    alt,
    triad,
    seventh,
    aliases,
  };
}

export function notes(modeName: ModeLiteral, tonic: NoteName) {
  return get(modeName).intervals.map((ivl) => transpose(tonic, ivl));
}

function chords(chords: string[]) {
  return (modeName: ModeLiteral, tonic: NoteName) => {
    const mode = get(modeName);
    if (mode.empty) return [];
    const triads = rotate(mode.modeNum, chords);
    const tonics = mode.intervals.map((i) => transpose(tonic, i));
    return triads.map((triad, i) => tonics[i] + triad);
  };
}

export const triads = chords(MODES.map((x) => x[4]));
export const seventhChords = chords(MODES.map((x) => x[5]));

export function distance(destination: ModeLiteral, source: ModeLiteral) {
  const from = get(source);
  const to = get(destination);
  if (from.empty || to.empty) return "";
  return simplify(transposeFifths("1P", to.alt - from.alt));
}

export function relativeTonic(
  destination: ModeLiteral,
  source: ModeLiteral,
  tonic: NoteName
) {
  return transpose(tonic, distance(destination, source));
}

export default {
  get,
  names,
  all,
  distance,
  relativeTonic,
  notes,
  triads,
  seventhChords,
  // deprecated
  entries,
  mode,
};

And if i do import * as Mode from "./node_modules/@tonaljs/mode/dist/index.js"; , it shows the error message : module is not defined

I'm not sure how to fix this.

razznblue commented 11 months ago

I assume you are using a node server? Hard to know exactly what might be causing your issue without seeing your project setup. The README might have helpful info on how to import Tonal into your project. I tested both of the examples below and work ok. Hopefully, that helps! 🤝

HTML (client side)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/tonal/browser/tonal.min.js"></script>
  <script>
    console.log(Tonal.Mode.all());
  </script>
</body>
</html>

Node.js(server-side)

const { Mode } = require('tonal');

console.log(Mode.all());