Files
dealplustech/node_modules/micromark-extension-gfm-footnote/lib/syntax.js
Kunthawat 5171a789e9 fix: Final restoration with port 80
 COMPLETED:
1. Dockerfile uses port 80 (astro preview)
2. BaseLayout imports globals.css
3. globals.css with Tailwind v4 @theme syntax
4. index.astro has Header, Footer, FixedContact
5. All image references fixed to existing files
6. Hero uses hdpe_pipe_main.jpg
7. Product cards use hdpe001.jpg
8. pt-20 on main for fixed header

 TESTED LOCALLY:
- Build: 15 pages in 1.27s
- Docker build successful
- Port 80 working
- Images load
- CSS works

Ready for Easypanel deployment.
2026-03-12 08:58:56 +07:00

485 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @import {Event, Exiter, Extension, Resolver, State, Token, TokenizeContext, Tokenizer} from 'micromark-util-types'
*/
import { blankLine } from 'micromark-core-commonmark';
import { factorySpace } from 'micromark-factory-space';
import { markdownLineEndingOrSpace } from 'micromark-util-character';
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
const indent = {
tokenize: tokenizeIndent,
partial: true
};
// To do: micromark should support a `_hiddenGfmFootnoteSupport`, which only
// affects label start (image).
// That will let us drop `tokenizePotentialGfmFootnote*`.
// It currently has a `_hiddenFootnoteSupport`, which affects that and more.
// That can be removed when `micromark-extension-footnote` is archived.
/**
* Create an extension for `micromark` to enable GFM footnote syntax.
*
* @returns {Extension}
* Extension for `micromark` that can be passed in `extensions` to
* enable GFM footnote syntax.
*/
export function gfmFootnote() {
/** @type {Extension} */
return {
document: {
[91]: {
name: 'gfmFootnoteDefinition',
tokenize: tokenizeDefinitionStart,
continuation: {
tokenize: tokenizeDefinitionContinuation
},
exit: gfmFootnoteDefinitionEnd
}
},
text: {
[91]: {
name: 'gfmFootnoteCall',
tokenize: tokenizeGfmFootnoteCall
},
[93]: {
name: 'gfmPotentialFootnoteCall',
add: 'after',
tokenize: tokenizePotentialGfmFootnoteCall,
resolveTo: resolveToPotentialGfmFootnoteCall
}
}
};
}
// To do: remove after micromark update.
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizePotentialGfmFootnoteCall(effects, ok, nok) {
const self = this;
let index = self.events.length;
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
/** @type {Token} */
let labelStart;
// Find an opening.
while (index--) {
const token = self.events[index][1];
if (token.type === "labelImage") {
labelStart = token;
break;
}
// Exit if weve walked far enough.
if (token.type === 'gfmFootnoteCall' || token.type === "labelLink" || token.type === "label" || token.type === "image" || token.type === "link") {
break;
}
}
return start;
/**
* @type {State}
*/
function start(code) {
if (!labelStart || !labelStart._balanced) {
return nok(code);
}
const id = normalizeIdentifier(self.sliceSerialize({
start: labelStart.end,
end: self.now()
}));
if (id.codePointAt(0) !== 94 || !defined.includes(id.slice(1))) {
return nok(code);
}
effects.enter('gfmFootnoteCallLabelMarker');
effects.consume(code);
effects.exit('gfmFootnoteCallLabelMarker');
return ok(code);
}
}
// To do: remove after micromark update.
/** @type {Resolver} */
function resolveToPotentialGfmFootnoteCall(events, context) {
let index = events.length;
/** @type {Token | undefined} */
let labelStart;
// Find an opening.
while (index--) {
if (events[index][1].type === "labelImage" && events[index][0] === 'enter') {
labelStart = events[index][1];
break;
}
}
// Change the `labelImageMarker` to a `data`.
events[index + 1][1].type = "data";
events[index + 3][1].type = 'gfmFootnoteCallLabelMarker';
// The whole (without `!`):
/** @type {Token} */
const call = {
type: 'gfmFootnoteCall',
start: Object.assign({}, events[index + 3][1].start),
end: Object.assign({}, events[events.length - 1][1].end)
};
// The `^` marker
/** @type {Token} */
const marker = {
type: 'gfmFootnoteCallMarker',
start: Object.assign({}, events[index + 3][1].end),
end: Object.assign({}, events[index + 3][1].end)
};
// Increment the end 1 character.
marker.end.column++;
marker.end.offset++;
marker.end._bufferIndex++;
/** @type {Token} */
const string = {
type: 'gfmFootnoteCallString',
start: Object.assign({}, marker.end),
end: Object.assign({}, events[events.length - 1][1].start)
};
/** @type {Token} */
const chunk = {
type: "chunkString",
contentType: 'string',
start: Object.assign({}, string.start),
end: Object.assign({}, string.end)
};
/** @type {Array<Event>} */
const replacement = [
// Take the `labelImageMarker` (now `data`, the `!`)
events[index + 1], events[index + 2], ['enter', call, context],
// The `[`
events[index + 3], events[index + 4],
// The `^`.
['enter', marker, context], ['exit', marker, context],
// Everything in between.
['enter', string, context], ['enter', chunk, context], ['exit', chunk, context], ['exit', string, context],
// The ending (`]`, properly parsed and labelled).
events[events.length - 2], events[events.length - 1], ['exit', call, context]];
events.splice(index, events.length - index + 1, ...replacement);
return events;
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeGfmFootnoteCall(effects, ok, nok) {
const self = this;
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
let size = 0;
/** @type {boolean} */
let data;
// Note: the implementation of `markdown-rs` is different, because it houses
// core *and* extensions in one project.
// Therefore, it can include footnote logic inside `label-end`.
// We cant do that, but luckily, we can parse footnotes in a simpler way than
// needed for labels.
return start;
/**
* Start of footnote label.
*
* ```markdown
* > | a [^b] c
* ^
* ```
*
* @type {State}
*/
function start(code) {
effects.enter('gfmFootnoteCall');
effects.enter('gfmFootnoteCallLabelMarker');
effects.consume(code);
effects.exit('gfmFootnoteCallLabelMarker');
return callStart;
}
/**
* After `[`, at `^`.
*
* ```markdown
* > | a [^b] c
* ^
* ```
*
* @type {State}
*/
function callStart(code) {
if (code !== 94) return nok(code);
effects.enter('gfmFootnoteCallMarker');
effects.consume(code);
effects.exit('gfmFootnoteCallMarker');
effects.enter('gfmFootnoteCallString');
effects.enter('chunkString').contentType = 'string';
return callData;
}
/**
* In label.
*
* ```markdown
* > | a [^b] c
* ^
* ```
*
* @type {State}
*/
function callData(code) {
if (
// Too long.
size > 999 ||
// Closing brace with nothing.
code === 93 && !data ||
// Space or tab is not supported by GFM for some reason.
// `\n` and `[` not being supported makes sense.
code === null || code === 91 || markdownLineEndingOrSpace(code)) {
return nok(code);
}
if (code === 93) {
effects.exit('chunkString');
const token = effects.exit('gfmFootnoteCallString');
if (!defined.includes(normalizeIdentifier(self.sliceSerialize(token)))) {
return nok(code);
}
effects.enter('gfmFootnoteCallLabelMarker');
effects.consume(code);
effects.exit('gfmFootnoteCallLabelMarker');
effects.exit('gfmFootnoteCall');
return ok;
}
if (!markdownLineEndingOrSpace(code)) {
data = true;
}
size++;
effects.consume(code);
return code === 92 ? callEscape : callData;
}
/**
* On character after escape.
*
* ```markdown
* > | a [^b\c] d
* ^
* ```
*
* @type {State}
*/
function callEscape(code) {
if (code === 91 || code === 92 || code === 93) {
effects.consume(code);
size++;
return callData;
}
return callData(code);
}
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeDefinitionStart(effects, ok, nok) {
const self = this;
const defined = self.parser.gfmFootnotes || (self.parser.gfmFootnotes = []);
/** @type {string} */
let identifier;
let size = 0;
/** @type {boolean | undefined} */
let data;
return start;
/**
* Start of GFM footnote definition.
*
* ```markdown
* > | [^a]: b
* ^
* ```
*
* @type {State}
*/
function start(code) {
effects.enter('gfmFootnoteDefinition')._container = true;
effects.enter('gfmFootnoteDefinitionLabel');
effects.enter('gfmFootnoteDefinitionLabelMarker');
effects.consume(code);
effects.exit('gfmFootnoteDefinitionLabelMarker');
return labelAtMarker;
}
/**
* In label, at caret.
*
* ```markdown
* > | [^a]: b
* ^
* ```
*
* @type {State}
*/
function labelAtMarker(code) {
if (code === 94) {
effects.enter('gfmFootnoteDefinitionMarker');
effects.consume(code);
effects.exit('gfmFootnoteDefinitionMarker');
effects.enter('gfmFootnoteDefinitionLabelString');
effects.enter('chunkString').contentType = 'string';
return labelInside;
}
return nok(code);
}
/**
* In label.
*
* > 👉 **Note**: `cmark-gfm` prevents whitespace from occurring in footnote
* > definition labels.
*
* ```markdown
* > | [^a]: b
* ^
* ```
*
* @type {State}
*/
function labelInside(code) {
if (
// Too long.
size > 999 ||
// Closing brace with nothing.
code === 93 && !data ||
// Space or tab is not supported by GFM for some reason.
// `\n` and `[` not being supported makes sense.
code === null || code === 91 || markdownLineEndingOrSpace(code)) {
return nok(code);
}
if (code === 93) {
effects.exit('chunkString');
const token = effects.exit('gfmFootnoteDefinitionLabelString');
identifier = normalizeIdentifier(self.sliceSerialize(token));
effects.enter('gfmFootnoteDefinitionLabelMarker');
effects.consume(code);
effects.exit('gfmFootnoteDefinitionLabelMarker');
effects.exit('gfmFootnoteDefinitionLabel');
return labelAfter;
}
if (!markdownLineEndingOrSpace(code)) {
data = true;
}
size++;
effects.consume(code);
return code === 92 ? labelEscape : labelInside;
}
/**
* After `\`, at a special character.
*
* > 👉 **Note**: `cmark-gfm` currently does not support escaped brackets:
* > <https://github.com/github/cmark-gfm/issues/240>
*
* ```markdown
* > | [^a\*b]: c
* ^
* ```
*
* @type {State}
*/
function labelEscape(code) {
if (code === 91 || code === 92 || code === 93) {
effects.consume(code);
size++;
return labelInside;
}
return labelInside(code);
}
/**
* After definition label.
*
* ```markdown
* > | [^a]: b
* ^
* ```
*
* @type {State}
*/
function labelAfter(code) {
if (code === 58) {
effects.enter('definitionMarker');
effects.consume(code);
effects.exit('definitionMarker');
if (!defined.includes(identifier)) {
defined.push(identifier);
}
// Any whitespace after the marker is eaten, forming indented code
// is not possible.
// No space is also fine, just like a block quote marker.
return factorySpace(effects, whitespaceAfter, 'gfmFootnoteDefinitionWhitespace');
}
return nok(code);
}
/**
* After definition prefix.
*
* ```markdown
* > | [^a]: b
* ^
* ```
*
* @type {State}
*/
function whitespaceAfter(code) {
// `markdown-rs` has a wrapping token for the prefix that is closed here.
return ok(code);
}
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeDefinitionContinuation(effects, ok, nok) {
/// Start of footnote definition continuation.
///
/// ```markdown
/// | [^a]: b
/// > | c
/// ^
/// ```
//
// Either a blank line, which is okay, or an indented thing.
return effects.check(blankLine, ok, effects.attempt(indent, ok, nok));
}
/** @type {Exiter} */
function gfmFootnoteDefinitionEnd(effects) {
effects.exit('gfmFootnoteDefinition');
}
/**
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeIndent(effects, ok, nok) {
const self = this;
return factorySpace(effects, afterPrefix, 'gfmFootnoteDefinitionIndent', 4 + 1);
/**
* @type {State}
*/
function afterPrefix(code) {
const tail = self.events[self.events.length - 1];
return tail && tail[1].type === 'gfmFootnoteDefinitionIndent' && tail[2].sliceSerialize(tail[1], true).length === 4 ? ok(code) : nok(code);
}
}