Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,35 @@ npm run --silent build-all
```

in order to compile the JavaScript files from the TypeScript source,
and build the component files from the JavaScript files.
and build the component files from the JavaScript files. Windows
users will need to use the command

``` bash
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
```

first in order to tell `pnpm` to use the `bash` shell for scripts that
it runs, as that is required by the build scripts that MathJax defines
in the `package.json` file. You may also need to use

``` bash
Set-ExecutionPolicy Unrestricted
```

to allow the scripts to run, if you receive errors about not being
able to run the scripts.

The build process requires MathJax to set up a symbolic link, and in
Windows, that requires permission, so you may receive an error message
to that effect. If so, you may need to run

``` bash
pnpm link:src
```

from a shell with administrator privileges. Once that is done, you
can run the build process from a non-administrator shell.


## Code Contributions

Expand Down
30 changes: 20 additions & 10 deletions components/bin/build
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ process.chdir(path.dirname(json));
const mjPath = path.relative(process.cwd(), path.resolve(__dirname, '..', '..', target));
const mjGlobal = path.join('..', mjPath, 'components', 'global.js');

/**
* Determine the module type
*/
function getType() {
const component = config.component || 'part';
if (component.match(/\/(svg|chtml|common)\/fonts\//)) return RegExp.$1 + '-font';
Expand All @@ -69,6 +72,13 @@ function getType() {
return component;
}

/**
* Convert Windows paths to unix paths
*/
const normalize = process.platform === 'win32'
? (file) => file.replace(/\\/g, '/')
: (file) => file;

/**
* Extract the configuration values
*/
Expand Down Expand Up @@ -100,7 +110,7 @@ let PACKAGE = [];
*/
function processList(base, dir, list, top = true) {
for (const item of list) {
const file = path.join(dir, item);
const file = normalize(path.join(dir, item));
if (!EXCLUDE.has(file)) {
const stat = fs.statSync(path.resolve(base, file));
if (stat.isDirectory()) {
Expand Down Expand Up @@ -183,9 +193,9 @@ function processParts(parts) {
function processLines(file, objects) {
if (objects.length === 0) return [];
const base = path.dirname(file).replace(/^\.$/, '');
const dir = (PREFIX ? path.join(PREFIX, base) : base);
const dir = (PREFIX ? noramlize(path.join(PREFIX, base)) : base);
const dots = dir.replace(/[^\/]+/g, '..') || '.';
const relative = path.join(dots, '..', JS, dir, path.basename(file)).replace(/\.ts$/, '.js');
const relative = normalize(path.join(dots, '..', JS, dir, path.basename(file))).replace(/\.ts$/, '.js');
const name = path.parse(file).name;
const lines = (target === 'mjs' ? [] : [
'"use strict";',
Expand Down Expand Up @@ -254,7 +264,7 @@ function getExtraDirectories() {
let prefix = '';
let indent = INDENT;
let postfix = '';
for (let name of PREFIX.split(/\//)) {
for (let name of PREFIX.split('/')) {
if (name.match(/[^a-zA-Z0-9]/)) {
name = `"${name}"`;
}
Expand All @@ -271,19 +281,19 @@ function getExtraDirectories() {
function processGlobal() {
console.info(' ' + COMPONENT + '.ts');
const lines = (target === 'cjs' ? [
`const {combineWithMathJax} = require('${GLOBAL}')`,
`const {VERSION} = require('${VERSION}');`,
`const {combineWithMathJax} = require('${normalize(GLOBAL)}')`,
`const {VERSION} = require('${normalize(VERSION)}');`,
'',
] : [
`import {combineWithMathJax} from '${GLOBAL}';`,
`import {VERSION} from '${VERSION}';`,
`import {combineWithMathJax} from '${normalize(GLOBAL)}';`,
`import {VERSION} from '${normalize(VERSION)}';`,
'',
]);
const [prefix, indent, postfix] = getExtraDirectories();
const packages = [];
PACKAGE = PACKAGE.sort(sortDir);
while (PACKAGE.length) {
const dir = path.dirname(PACKAGE[0]).split(path.sep)[0];
const dir = path.dirname(PACKAGE[0]).split('/')[0];
packages.push(processPackage(lines, indent, dir));
}
const name = (ID.match(/[^a-zA-Z0-9_]/) ? `"${ID}"` : ID);
Expand Down Expand Up @@ -337,7 +347,7 @@ function processPackage(lines, space, dir) {
if (path.dirname(PACKAGE[0]) === dir) {
const file = PACKAGE.shift();
const name = path.basename(file);
let relativefile = path.join('..', JS, dir, name).replace(/\.ts$/, '.js')
const relativefile = normalize(path.join('..', JS, dir, name).replace(/\.ts$/, '.js'));
const component = 'module' + (++importCount);
lines.push(
target === 'cjs' ?
Expand Down
9 changes: 8 additions & 1 deletion components/bin/makeAll
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ function fileRegExp(name) {
return new RegExp(name.replace(/([\\.{}[\]()?*^$])/g, '\\$1'), 'g');
}

/**
* Options for the execSync() function
*/
const execOptions = process.platform === 'win32'
? { shell: `${process.env.ProgramFiles}\\Git\\bin\\bash.exe` }
: {};

/**
* Get the current working directory
*/
Expand Down Expand Up @@ -196,7 +203,7 @@ function processSubdirs(dir, action, config) {
* Run a command on a given directory
*/
function run(cmd, dir) {
return execSync(cmd + ` '${path.relative('.', dir).replace(/'/g, '\\\'')}'`);
return execSync(cmd + ` '${path.relative('.', dir).replace(/'/g, '\\\'')}'`, execOptions);
}

/**
Expand Down
30 changes: 16 additions & 14 deletions components/bin/pack
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

const fs = require('fs');
const path = require('path');
const {spawn, execSync} = require('child_process');
const {spawn} = require('child_process');

/**
* The module type to use ('cjs' or 'mjs')
Expand Down Expand Up @@ -64,14 +64,7 @@ const rootRE = fileRegExp(path.dirname(jsPath));
const nodeRE = /^.*\/node_modules/;
const fontRE = new RegExp('^.*\\/(mathjax-[^\/-]*)(?:-font)?\/(build|[cm]js)');

/**
* Find the directory where npx runs (so we know where "npx webpack" will run)
* (We use npx rather than pnpm here as it seems that pnpm doesn't
* find the executable from a node_modules directory higher than the
* first package.json, and extensions and fonts can have their own
* package.json.)
*/
const packDir = String(execSync('npx node -e "console.log(process.cwd())"'));
const packDir = process.cwd();

/**
* @param {string} dir The directory to pack
Expand All @@ -80,11 +73,20 @@ const packDir = String(execSync('npx node -e "console.log(process.cwd())"'));
async function readJSON(dir) {
return new Promise((ok, fail) => {
const buffer = [];
const child = spawn('npx', [
'webpack', '--env', `dir=${path.relative(packDir, path.resolve(dir))}`,
'--env', `bundle=${bundle}`, '--json',
'-c', path.relative(packDir, path.join(compPath, 'webpack.config.' + target))
]);
const child = spawn(
'npx',
[
'webpack',
'--env', `dir=${path.relative(packDir, path.resolve(dir))}`,
'--env', `bundle=${bundle}`,
'--json',
'-c', path.relative(packDir, path.join(compPath, 'webpack.config.' + target))
],
{
cwd: packDir,
shell: true,
}
);
child.stdout.on('data', (data) => buffer.push(String(data)));
child.stderr.on('data', (data) => console.error(String(data)));
child.on('close', (code) => {
Expand Down
8 changes: 4 additions & 4 deletions components/mjs/a11y/speech/speech.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import './lib/speech.js';

import {combineDefaults} from '#js/components/global.js';
import {Package} from '#js/components/package.js';
import {hasWindow} from '#js/util/context.js';
import {context} from '#js/util/context.js';
import {SpeechHandler} from '#js/a11y/speech.js';

if (MathJax.loader) {
let path = Package.resolvePath('[sre]', false);
let maps = Package.resolvePath('[mathmaps]', false);
if (hasWindow) {
if (context.window) {
path = new URL(path, location).href;
maps = new URL(maps, location).href;
} else {
const REQUIRE = typeof require !== 'undefined' ? require : MathJax.config.loader.require;
if (REQUIRE?.resolve) {
path = REQUIRE.resolve(`${path}/require.mjs`).replace(/\/[^\/]*$/, '');
maps = REQUIRE.resolve(`${maps}/base.json`).replace(/\/[^\/]*$/, '');
path = context.path(REQUIRE.resolve(`${path}/require.mjs`)).replace(/\/[^\/]*$/, '');
maps = context.path(REQUIRE.resolve(`${maps}/base.json`)).replace(/\/[^\/]*$/, '');
} else {
path = maps = '';
}
Expand Down
2 changes: 1 addition & 1 deletion components/mjs/node-main/node-main-setup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ global.require = createRequire(import.meta.url);
const path = require("path");

if (!global.MathJax) global.MathJax = {};
global.MathJax.__dirname = path.dirname(new URL(import.meta.url).pathname);
global.MathJax.__dirname = path.dirname(new URL(import.meta.url).pathname);
3 changes: 2 additions & 1 deletion components/mjs/node-main/node-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ import '../startup/init.js';
import {Loader, CONFIG} from '#js/components/loader.js';
import {Package} from '#js/components/package.js';
import {combineDefaults, combineConfig} from '#js/components/global.js';
import {context} from '#js/util/context.js';
import '../core/core.js';
import '../adaptors/liteDOM/liteDOM.js';
import {source} from '../source.js';

const MathJax = global.MathJax;

const path = eval('require("path")'); // get path from node, not webpack
const dir = MathJax.config.__dirname; // set up by node-main.mjs or node-main.cjs
const dir = context.path(MathJax.config.__dirname); // set up by node-main.mjs or node-main.cjs

/*
* Set up the initial configuration
Expand Down
2 changes: 1 addition & 1 deletion components/mjs/source-lab.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
* limitations under the License.
*/

export const src = String(new URL('.', import.meta.url)).replace(/\/$/, '');
export const dirname = String(new URL('.', import.meta.url)).replace(/\/$/, '');
2 changes: 1 addition & 1 deletion components/mjs/source.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
* limitations under the License.
*/

module.exports.src = __dirname;
module.exports.dirname = __dirname;
2 changes: 1 addition & 1 deletion components/mjs/source.d.cts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export declare const src: string;
export declare const dirname: string;
4 changes: 3 additions & 1 deletion components/mjs/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
* limitations under the License.
*/

import {src} from '#source/source.cjs';
import {dirname} from '#source/source.cjs';
import {context} from '#js/util/context.js';
const src = context.path(dirname);

export const source = {
'core': `${src}/core/core.js`,
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@
"copy:mml3": "copy() { pnpm -s log:single 'Copying legacy code MathML3'; pnpm copyfiles -u 1 ts/input/mathml/mml3/mml3.sef.json $1; }; copy",
"copy:pkg": "copy() { pnpm -s log:single \"Copying package.json to $1\"; pnpm copyfiles -u 2 components/bin/package.json $1; }; copy",
"=============================================================================== log": "",
"log:comp": "log() { echo \\\\033[32m$1\\\\033[0m; }; log",
"log:header": "log() { echo '============='; echo $1; echo '============='; }; log",
"log:single": "log() { echo \\\\033[34m--$1\\\\033[0m; }; log",
"log:comp": "log() { echo \u001B[32m$1\u001B[0m; }; log",
"log:header": "log() { echo '\u001B[1m============='; echo $1; echo '=============\u001B[0m'; }; log",
"log:single": "log() { echo \u001B[94m--$1\u001B[0m; }; log",
"=============================================================================== cjs": "",
"cjs:build": "pnpm -s log:header 'Building cjs'; pnpm -s cjs:src:build && pnpm -s cjs:components:build",
"cjs:bundle:clean": "pnpm clean:dir bundle-cjs",
Expand Down
2 changes: 1 addition & 1 deletion testsuite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"dependencies": {
"@jest/globals": "^29.7.0",
"@mathjax/mathjax-bbm-font-extension": "0.4.2-beta.8",
"@mathjax/mathjax-bbm-font-extension": "^4.0.0",
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"ts-jest": "^29.3.4",
Expand Down
10 changes: 5 additions & 5 deletions testsuite/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions testsuite/tests/util/Context-android.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ describe('context object', () => {

test('context', async () => {
let {context, hasWindow} = await import("#js/util/context.js");
expect(context.path('C:\\test.js')).toBe('C:\\test.js');
delete context.path;
expect(context).toEqual({window: window, document: window.document, os: 'Unix'});
expect(hasWindow).toBe(true);
});
Expand Down
2 changes: 2 additions & 0 deletions testsuite/tests/util/Context-browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ describe('context object', () => {

test('context', async () => {
let {context, hasWindow} = await import("#js/util/context.js");
expect(context.path('C:\\test.js')).toBe('C:\\test.js');
delete context.path;
expect(context).toEqual({window: window, document: window.document, os: 'Unix'});
expect(hasWindow).toBe(true);
});
Expand Down
15 changes: 15 additions & 0 deletions testsuite/tests/util/Context-node-unknown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { describe, test, expect } from '@jest/globals';

global.process = {...process, platform: 'test'} as any;

describe('context object', () => {

test('context', async () => {
let {context, hasWindow} = await import("#js/util/context.js");
expect(context.path('C:\\test.js')).toBe('C:\\test.js');
delete context.path;
expect(context).toEqual({window: null, document: null, os: 'test'});
expect(hasWindow).toBe(false);
});

});
22 changes: 21 additions & 1 deletion testsuite/tests/util/Context-node.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { describe, test, expect } from '@jest/globals';
import { context, hasWindow } from '#js/util/context.js';

const OS = {
'linux': 'Unix',
'android': 'Unix',
'aix': 'Unix',
'freebsd': 'Unix',
'netbsd': 'Unix',
'openbsd': 'Unix',
'sunos': 'Unix',
'darwin': 'MacOS',
'win32': 'Windows',
'cygwin': 'Windows',
'haiku': 'unknown',
}[process.platform] || process.platform;

describe('context object', () => {

test('context', () => {
expect(context).toEqual({window: null, document: null, os: 'unknown'});
if (OS === 'Windows') {
expect(context.path('C:\\test.js')).toBe('C:/test.js');
} else {
expect(context.path('C:\\test.js')).toBe('C:\\test.js');
}
delete context.path;
expect(context).toEqual({window: null, document: null, os: OS});
expect(hasWindow).toBe(false);
});

Expand Down
Loading