Просмотр исходного кода

refs 120355: enable attachment refs routes

Futa Arai 3 лет назад
Родитель
Сommit
8411984321
24 измененных файлов с 83 добавлено и 920 удалено
  1. 2 0
      apps/app/src/server/crowi/index.js
  2. 0 10
      packages/plugin-attachment-refs/src/client-entry.js
  3. 0 28
      packages/plugin-attachment-refs/src/client/css/index.css
  4. 0 199
      packages/plugin-attachment-refs/src/client/js/components/AttachmentList.jsx
  5. 0 214
      packages/plugin-attachment-refs/src/client/js/components/ExtractedAttachments.jsx
  6. 0 24
      packages/plugin-attachment-refs/src/client/js/util/GalleryContext.js
  7. 0 57
      packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPostRenderInterceptor.jsx
  8. 0 59
      packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPreRenderInterceptor.js
  9. 0 249
      packages/plugin-attachment-refs/src/client/js/util/RefsContext.js
  10. 0 21
      packages/plugin-attachment-refs/src/client/js/util/TagCacheManagerFactory.js
  11. 0 11
      packages/plugin-attachment-refs/src/index.js
  12. 0 4
      packages/plugin-attachment-refs/src/server-entry.js
  13. 0 4
      packages/plugin-attachment-refs/src/server/routes/index.js
  14. 0 11
      packages/plugin-attachment-refs/src/utils/logger/index.ts
  15. 0 0
      packages/remark-attachment-refs/.eslintignore
  16. 0 0
      packages/remark-attachment-refs/.gitignore
  17. 1 1
      packages/remark-attachment-refs/README.md
  18. 30 0
      packages/remark-attachment-refs/client.txt
  19. 5 3
      packages/remark-attachment-refs/package.json
  20. 17 0
      packages/remark-attachment-refs/src/server/index.ts
  21. 14 11
      packages/remark-attachment-refs/src/server/routes/refs.ts
  22. 0 0
      packages/remark-attachment-refs/tsconfig.json
  23. 8 8
      packages/remark-attachment-refs/vite.server.config.ts
  24. 6 6
      turbo.json

+ 2 - 0
apps/app/src/server/crowi/index.js

@@ -3,6 +3,7 @@ import http from 'http';
 import path from 'path';
 import path from 'path';
 
 
 import { createTerminus } from '@godaddy/terminus';
 import { createTerminus } from '@godaddy/terminus';
+import attachmentRoutes from '@growi/remark-attachment-refs/dist/server';
 import lsxRoutes from '@growi/remark-lsx/dist/server';
 import lsxRoutes from '@growi/remark-lsx/dist/server';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 import next from 'next';
 import next from 'next';
@@ -531,6 +532,7 @@ Crowi.prototype.setupTerminus = function(server) {
 
 
 Crowi.prototype.setupRoutesForPlugins = function() {
 Crowi.prototype.setupRoutesForPlugins = function() {
   lsxRoutes(this, this.express);
   lsxRoutes(this, this.express);
+  attachmentRoutes(this, this.express);
 };
 };
 
 
 /**
 /**

+ 0 - 10
packages/plugin-attachment-refs/src/client-entry.js

@@ -1,10 +0,0 @@
-import RefsPostRenderInterceptor from './client/js/util/Interceptor/RefsPostRenderInterceptor';
-import RefsPreRenderInterceptor from './client/js/util/Interceptor/RefsPreRenderInterceptor';
-
-export default () => {
-  // add interceptors
-  global.interceptorManager.addInterceptors([
-    new RefsPreRenderInterceptor(),
-    new RefsPostRenderInterceptor(),
-  ]);
-};

+ 0 - 28
packages/plugin-attachment-refs/src/client/css/index.css

@@ -1,28 +0,0 @@
-@keyframes attachement-refs-fadeIn {
-  0% {opacity: .2}
-  100% {opacity: .9}
-}
-
-.attachment-refs .attachement-refs-blink {
-  animation: attachement-refs-fadeIn 1s ease 0s infinite alternate;
-}
-
-
-.attachment-refs li.attachment {
-  list-style: none;
-}
-
-.attachment-refs .attachment-userpicture {
-  line-height: 1.7em;
-  vertical-align: bottom;
-}
-
-.attachment-refs .page-meta {
-  font-size: 0.95em;
-}
-
-.attachment-refs .attachment-filetype {
-  padding: 1px 5px;
-  margin: 0 0 0 4px;
-  font-weight: normal;
-}

+ 0 - 199
packages/plugin-attachment-refs/src/client/js/components/AttachmentList.jsx

@@ -1,199 +0,0 @@
-import React from 'react';
-
-import { Attachment } from '@growi/ui/dist/components/Attachment';
-import axios from 'axios'; // import axios from growi dependencies
-import PropTypes from 'prop-types';
-
-// eslint-disable-next-line import/no-unresolved
-
-import RefsContext from '../util/RefsContext';
-import TagCacheManagerFactory from '../util/TagCacheManagerFactory';
-
-// eslint-disable-next-line no-unused-vars
-
-import ExtractedAttachments from './ExtractedAttachments';
-
-import styles from '../../css/index.css';
-
-const AttachmentLink = Attachment;
-
-export default class AttachmentList extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      isLoading: false,
-      isLoaded: false,
-      isError: false,
-      errorMessage: null,
-
-      attachments: [],
-    };
-
-    this.tagCacheManager = TagCacheManagerFactory.getInstance();
-  }
-
-  async UNSAFE_componentWillMount() {
-    const { refsContext } = this.props;
-
-    // get state object cache
-    const stateCache = this.tagCacheManager.getStateCache(refsContext);
-
-    // check cache exists
-    if (stateCache != null) {
-      // restore state
-      this.setState({
-        ...stateCache,
-        isLoading: false,
-      });
-      // parse with no effect
-      try {
-        refsContext.parse();
-      }
-      catch (err) {
-        // do nothing
-      }
-
-      return; // go to render()
-    }
-
-    // parse
-    try {
-      refsContext.parse();
-    }
-    catch (err) {
-      this.setState({
-        isError: true,
-        errorMessage: err.toString(),
-      });
-
-      // store to sessionStorage
-      this.tagCacheManager.cacheState(refsContext, this.state);
-
-      return;
-    }
-
-    this.loadContents();
-  }
-
-  async loadContents() {
-    const { refsContext } = this.props;
-
-    let res;
-    try {
-      this.setState({ isLoading: true });
-
-      if (refsContext.isSingle) {
-        res = await axios.get('/_api/plugin/ref', {
-          params: {
-            pagePath: refsContext.pagePath,
-            fileNameOrId: refsContext.fileNameOrId,
-            options: refsContext.options,
-          },
-        });
-        this.setState({
-          attachments: [res.data.attachment],
-        });
-      }
-      else {
-        res = await axios.get('/_api/plugin/refs', {
-          params: {
-            prefix: refsContext.prefix,
-            pagePath: refsContext.pagePath,
-            options: refsContext.options,
-          },
-        });
-        this.setState({
-          attachments: res.data.attachments,
-        });
-      }
-
-      this.setState({
-        isLoaded: true,
-      });
-    }
-    catch (err) {
-      this.setState({
-        isError: true,
-        errorMessage: err.response.data,
-      });
-
-      return;
-    }
-    finally {
-      this.setState({ isLoading: false });
-
-      // store to sessionStorage
-      this.tagCacheManager.cacheState(refsContext, this.state);
-    }
-
-  }
-
-  renderNoAttachmentsMessage() {
-    const { refsContext } = this.props;
-
-    let message;
-
-    if (refsContext.prefix != null) {
-      message = `${refsContext.prefix} and descendant pages have no attachments`;
-    }
-    else {
-      message = `${refsContext.pagePath} has no attachments`;
-    }
-
-    return (
-      <div className="text-muted">
-        <small>
-          <i className="fa fa-fw fa-info-circle" aria-hidden="true"></i>
-          {message}
-        </small>
-      </div>
-    );
-  }
-
-  renderContents() {
-    const { refsContext } = this.props;
-
-    if (this.state.isLoading) {
-      return (
-        <div className="text-muted">
-          <i className="fa fa-spinner fa-pulse mr-1"></i>
-          <span className="attachment-refs-blink">{refsContext.tagExpression}</span>
-        </div>
-      );
-    }
-    if (this.state.errorMessage != null) {
-      return (
-        <div className="text-warning">
-          <i className="fa fa-exclamation-triangle fa-fw"></i>
-          {refsContext.tagExpression} (-&gt; <small>{this.state.errorMessage}</small>)
-        </div>
-      );
-    }
-
-    if (this.state.isLoaded) {
-      const { attachments } = this.state;
-
-      // no attachments
-      if (attachments.length === 0) {
-        return this.renderNoAttachmentsMessage();
-      }
-
-      return (refsContext.isExtractImg)
-        ? <ExtractedAttachments attachments={attachments} refsContext={refsContext} />
-        : attachments.map((attachment) => {
-          return <AttachmentLink key={attachment._id} attachment={attachment} />;
-        });
-    }
-  }
-
-  render() {
-    return <div className="attachment-refs">{this.renderContents()}</div>;
-  }
-
-}
-
-AttachmentList.propTypes = {
-  refsContext: PropTypes.instanceOf(RefsContext).isRequired,
-};

+ 0 - 214
packages/plugin-attachment-refs/src/client/js/components/ExtractedAttachments.jsx

@@ -1,214 +0,0 @@
-import React from 'react';
-
-import PropTypes from 'prop-types';
-import Carousel, { Modal, ModalGateway } from 'react-images';
-
-import RefsContext from '../util/RefsContext';
-
-/**
- *  1. when 'fileFormat' is image, render Attachment as an image
- *  2. when 'fileFormat' is not image, render Attachment as an Attachment component
- */
-export default class ExtractedAttachments extends React.PureComponent {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      showCarousel: false,
-      currentIndex: null,
-    };
-  }
-
-  imageClickedHandler(index) {
-    this.setState({
-      showCarousel: true,
-      currentIndex: index,
-    });
-  }
-
-  getAttachmentsFilteredByFormat() {
-    return this.props.attachments
-      .filter(attachment => attachment.fileFormat.startsWith('image/'));
-  }
-
-  getClassesAndStylesForNonGrid() {
-    const { refsContext } = this.props;
-    const { options } = refsContext;
-
-    const {
-      width,
-      height,
-      'max-width': maxWidth,
-      'max-height': maxHeight,
-      display = 'block',
-    } = options;
-
-    const containerStyles = {
-      width, height, maxWidth, maxHeight, display,
-    };
-
-    const imageClasses = [];
-    const imageStyles = {
-      width, height, maxWidth, maxHeight,
-    };
-
-    return {
-      containerStyles,
-      imageClasses,
-      imageStyles,
-    };
-  }
-
-  getClassesAndStylesForGrid() {
-    const { refsContext } = this.props;
-    const { options } = refsContext;
-
-    const {
-      'max-width': maxWidth,
-      'max-height': maxHeight,
-    } = options;
-
-    const containerStyles = {
-      width: refsContext.getOptGridWidth(),
-      height: refsContext.getOptGridHeight(),
-      maxWidth,
-      maxHeight,
-    };
-
-    const imageClasses = ['w-100', 'h-100'];
-    const imageStyles = {
-      objectFit: 'cover',
-      maxWidth,
-      maxHeight,
-    };
-
-    return {
-      containerStyles,
-      imageClasses,
-      imageStyles,
-    };
-  }
-
-  /**
-   * wrapper method for getClassesAndStylesForGrid/getClassesAndStylesForNonGrid
-   */
-  getClassesAndStyles() {
-    const { refsContext } = this.props;
-    const { options } = refsContext;
-
-    return (options.grid != null)
-      ? this.getClassesAndStylesForGrid()
-      : this.getClassesAndStylesForNonGrid();
-  }
-
-  renderExtractedImage(attachment, index) {
-    const { refsContext } = this.props;
-    const { options } = refsContext;
-
-    // determine alt
-    let alt = refsContext.isSingle ? options.alt : undefined; // use only when single mode
-    alt = alt || attachment.originalName; //                     use 'originalName' if options.alt is not specified
-
-    // get styles
-    const {
-      containerStyles, imageClasses, imageStyles,
-    } = this.getClassesAndStyles();
-
-    // carousel settings
-    let onClick;
-    if (options['no-carousel'] == null) {
-      // pointer cursor
-      Object.assign(containerStyles, { cursor: 'pointer' });
-      // set click handler
-      onClick = () => {
-        this.imageClickedHandler(index);
-      };
-    }
-
-    return (
-      <div key={attachment._id} style={containerStyles} onClick={onClick}>
-        <img src={attachment.filePathProxied} alt={alt} className={imageClasses.join(' ')} style={imageStyles} />
-      </div>
-    );
-  }
-
-  renderCarousel() {
-    const { options } = this.props.refsContext;
-    const withCarousel = options['no-carousel'] == null;
-
-    const { showCarousel, currentIndex } = this.state;
-
-    const images = this.getAttachmentsFilteredByFormat()
-      .map((attachment) => {
-        return { src: attachment.filePathProxied };
-      });
-
-    // overwrite react-images modal styles
-    const zIndex = 1030; // > grw-navbar
-    const modalStyles = {
-      blanket: (styleObj) => {
-        return Object.assign(styleObj, { zIndex });
-      },
-      positioner: (styleObj) => {
-        return Object.assign(styleObj, { zIndex });
-      },
-    };
-
-    return (
-      <ModalGateway>
-        { withCarousel && showCarousel && (
-          <Modal styles={modalStyles} onClose={() => { this.setState({ showCarousel: false }) }}>
-            <Carousel views={images} currentIndex={currentIndex} />
-          </Modal>
-        ) }
-      </ModalGateway>
-    );
-  }
-
-  render() {
-    const { refsContext } = this.props;
-    const { options } = refsContext;
-    const {
-      grid,
-      'grid-gap': gridGap,
-    } = options;
-
-    const styles = {};
-
-    // Grid mode
-    if (grid != null) {
-
-      const gridTemplateColumns = (refsContext.isOptGridColumnEnabled())
-        ? `repeat(${refsContext.getOptGridColumnsNum()}, 1fr)`
-        : `repeat(auto-fill, ${refsContext.getOptGridWidth()})`;
-
-      Object.assign(styles, {
-        display: 'grid',
-        gridTemplateColumns,
-        gridAutoRows: '1fr',
-        gridGap,
-      });
-
-    }
-
-    const contents = this.getAttachmentsFilteredByFormat()
-      .map((attachment, index) => this.renderExtractedImage(attachment, index));
-
-    return (
-      <React.Fragment>
-        <div style={styles}>
-          {contents}
-        </div>
-
-        { this.renderCarousel() }
-      </React.Fragment>
-    );
-  }
-
-}
-
-ExtractedAttachments.propTypes = {
-  attachments: PropTypes.arrayOf(PropTypes.object).isRequired,
-  refsContext: PropTypes.instanceOf(RefsContext).isRequired,
-};

+ 0 - 24
packages/plugin-attachment-refs/src/client/js/util/GalleryContext.js

@@ -1,24 +0,0 @@
-import RefsContext from './RefsContext';
-
-/**
- * Context Object class for $gallery()
- */
-export default class GalleryContext extends RefsContext {
-
-  /**
-   * @param {object|TagContext|RefsContext} initArgs
-   */
-  constructor(initArgs, fromPagePath) {
-    super(initArgs);
-
-    this.options = {
-      grid: 'col-4',
-      'grid-gap': '1px',
-    };
-  }
-
-  get isExtractImg() {
-    return true;
-  }
-
-}

+ 0 - 57
packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPostRenderInterceptor.jsx

@@ -1,57 +0,0 @@
-import { BasicInterceptor } from '@growi/core';
-import { createRoot } from 'react-dom/client';
-
-import AttachmentList from '../../components/AttachmentList';
-import GalleryContext from '../GalleryContext';
-import RefsContext from '../RefsContext';
-
-
-/**
- * The interceptor for refs
- *
- *  render React DOM
- */
-export default class RefsPostRenderInterceptor extends BasicInterceptor {
-
-  /**
-   * @inheritdoc
-   */
-  isInterceptWhen(contextName) {
-    return (
-      contextName === 'postRenderHtml'
-      || contextName === 'postRenderPreviewHtml'
-    );
-  }
-
-  /**
-   * @inheritdoc
-   */
-  async process(contextName, ...args) {
-    const context = Object.assign(args[0]); // clone
-
-    // forEach keys of tagContextMap
-    Object.keys(context.tagContextMap).forEach((domId) => {
-      const elem = document.getElementById(domId);
-
-      if (elem) {
-        const tagContext = context.tagContextMap[domId];
-
-        // instanciate RefsContext from context
-        const refsContext = (tagContext.method === 'gallery')
-          ? new GalleryContext(tagContext || {})
-          : new RefsContext(tagContext || {});
-        refsContext.fromPagePath = context.pagePath ?? context.currentPathname;
-
-        this.renderReactDom(refsContext, elem);
-      }
-    });
-
-    return context;
-  }
-
-  renderReactDom(refsContext, elem) {
-    const root = createRoot(elem);
-    elem.render(<AttachmentList appContainer={this.appContainer} refsContext={refsContext} />);
-  }
-
-}

+ 0 - 59
packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPreRenderInterceptor.js

@@ -1,59 +0,0 @@
-import { findTagAndReplace, BasicInterceptor } from '@growi/core';
-
-import TagCacheManagerFactory from '../TagCacheManagerFactory';
-
-/**
- * The interceptor for refs
- *
- *  replace refs tag to a React target element
- */
-export default class RefsPreRenderInterceptor extends BasicInterceptor {
-
-  /**
-   * @inheritdoc
-   */
-  isInterceptWhen(contextName) {
-    return (
-      contextName === 'preRenderHtml'
-      || contextName === 'preRenderPreviewHtml'
-    );
-  }
-
-  /**
-   * @inheritdoc
-   */
-  isProcessableParallel() {
-    return false;
-  }
-
-  /**
-   * @inheritdoc
-   */
-  async process(contextName, ...args) {
-    const context = Object.assign(args[0]); // clone
-    const parsedHTML = context.parsedHTML;
-    this.initializeCache(contextName);
-
-    const tagPattern = /ref|refs|refimg|refsimg|gallery/;
-    const result = findTagAndReplace(tagPattern, parsedHTML);
-
-    context.parsedHTML = result.html;
-    context.tagContextMap = result.tagContextMap;
-
-    return context;
-  }
-
-  /**
-   * initialize cache
-   *  when contextName is 'preRenderHtml'         -> clear cache
-   *  when contextName is 'preRenderPreviewHtml'  -> doesn't clear cache
-   *
-   * @param {string} contextName
-   */
-  initializeCache(contextName) {
-    if (contextName === 'preRenderHtml') {
-      TagCacheManagerFactory.getInstance().clearAllStateCaches();
-    }
-  }
-
-}

+ 0 - 249
packages/plugin-attachment-refs/src/client/js/util/RefsContext.js

@@ -1,249 +0,0 @@
-import * as url from 'url';
-
-import {
-  TagContext, ArgsParser, OptionParser, pathUtils,
-} from '@growi/core';
-
-const GRID_DEFAULT_TRACK_WIDTH = 64;
-const GRID_AVAILABLE_OPTIONS_LIST = [
-  'autofill',
-  'autofill-xs',
-  'autofill-sm',
-  'autofill-md',
-  'autofill-lg',
-  'autofill-xl',
-  'col-2',
-  'col-3',
-  'col-4',
-  'col-5',
-  'col-6',
-];
-
-/**
- * Context Object class for $refs() and $refsimg()
- */
-export default class RefsContext extends TagContext {
-
-  /**
-   * @param {object|TagContext|RefsContext} initArgs
-   */
-  constructor(initArgs, fromPagePath) {
-    super(initArgs);
-
-    this.fromPagePath = fromPagePath;
-
-    // initialized after parse()
-    this.pagePath = null;
-    this.isParsed = null;
-    this.options = {};
-  }
-
-  get isSingle() {
-    return this.method.match(/^(ref|refimg)$/);
-  }
-
-  get isExtractImg() {
-    return this.method.match(/^(refimg|refsimg)$/);
-  }
-
-  parse() {
-    if (this.isParsed) {
-      return;
-    }
-
-    const parsedArgs = ArgsParser.parse(this.args);
-    this.options = Object.assign(this.options, parsedArgs.options);
-
-    if (this.isSingle) {
-      this.parseForSingle(parsedArgs);
-    }
-    else {
-      this.parseForMulti(parsedArgs);
-    }
-
-    this.isParsed = true;
-  }
-
-  /**
-   * parse method for 'ref' and 'refimg'
-   */
-  parseForSingle(parsedArgs) {
-    // determine fileNameOrId
-    // order:
-    //   1: ref(file=..., ...)
-    //   2: ref(id=..., ...)
-    //   2: refs(firstArgs, ...)
-    this.fileNameOrId = this.options.file || this.options.id
-      || ((parsedArgs.firstArgsValue === true) ? parsedArgs.firstArgsKey : undefined);
-
-    if (this.fileNameOrId == null) {
-      throw new Error('\'file\' or \'id\' is not specified. Set first argument or specify \'file\' or \'id\' option');
-    }
-
-    // determine pagePath
-    // order:
-    //   1: ref(page=..., ...)
-    //   2: constructor argument
-    const specifiedPath = this.options.page || this.fromPagePath;
-    this.pagePath = this.getAbsolutePathFor(specifiedPath);
-  }
-
-  /**
-   * parse method for 'refs' and 'refsimg'
-   */
-  parseForMulti(parsedArgs) {
-    if (this.options.prefix) {
-      this.prefix = this.resolvePath(this.options.prefix);
-    }
-    else {
-      // determine pagePath
-      // order:
-      //   1: ref(page=..., ...)
-      //   2: refs(firstArgs, ...)
-      //   3: constructor argument
-      const specifiedPath = this.options.page
-      || ((parsedArgs.firstArgsValue === true) ? parsedArgs.firstArgsKey : undefined)
-      || this.fromPagePath;
-
-      this.pagePath = this.getAbsolutePathFor(specifiedPath);
-    }
-
-    if (this.options.grid != null && this.getOptGrid() == null) {
-      throw new Error('\'grid\' option is invalid. '
-        + 'Available value is: \'autofill-( xs | sm | md | lg | xl )\' or \'col-( 2 | 3 | 4 | 5 | 6 )\'');
-    }
-  }
-
-  // resolve pagePath
-  //   when `fromPagePath`=/hoge and `specifiedPath`=./fuga,
-  //        `pagePath` to be /hoge/fuga
-  //   when `fromPagePath`=/hoge and `specifiedPath`=/fuga,
-  //        `pagePath` to be /fuga
-  //   when `fromPagePath`=/hoge and `specifiedPath`=undefined,
-  //        `pagePath` to be /hoge
-  resolvePath(pagePath) {
-    return decodeURIComponent(url.resolve(pathUtils.addTrailingSlash(this.fromPagePath), pagePath));
-  }
-
-  getOptDepth() {
-    if (this.options.depth === undefined) {
-      return undefined;
-    }
-    return OptionParser.parseRange(this.options.depth);
-  }
-
-  getOptGrid() {
-    const { grid } = this.options;
-    return GRID_AVAILABLE_OPTIONS_LIST.find(item => item === grid);
-  }
-
-  isOptGridColumnEnabled() {
-    const optGrid = this.getOptGrid();
-    return (optGrid != null) && optGrid.startsWith('col-');
-  }
-
-  getOptGridColumnsNum() {
-    const { grid } = this.options;
-
-    let columnsNum = null;
-
-    switch (grid) {
-      case 'col-2':
-        columnsNum = 2;
-        break;
-      case 'col-3':
-        columnsNum = 3;
-        break;
-      case 'col-4':
-        columnsNum = 4;
-        break;
-      case 'col-5':
-        columnsNum = 5;
-        break;
-      case 'col-6':
-        columnsNum = 6;
-        break;
-    }
-
-    return columnsNum;
-  }
-
-  /**
-   * return auto-calculated grid width
-   * rules:
-   *  1. when column mode (e.g. col-6, col5, ...), the width specification is disabled
-   *  2. when width option is set, return it
-   *  3. otherwise, the mode should be autofill and the width will be calculated according to the size
-   */
-  getOptGridWidth() {
-    const grid = this.getOptGrid();
-    const { width } = this.options;
-
-    // when Grid column mode
-    if (this.isOptGridColumnEnabled()) {
-      // not specify and ignore width
-      return undefined;
-    }
-
-    // when width is specified
-    if (width != null) {
-      return width;
-    }
-
-    // when Grid autofill mode
-    let autofillMagnification = 1;
-
-    switch (grid) {
-      case 'autofill-xl':
-        autofillMagnification = 3;
-        break;
-      case 'autofill-lg':
-        autofillMagnification = 2;
-        break;
-      case 'autofill-sm':
-        autofillMagnification = 0.75;
-        break;
-      case 'autofill-xs':
-        autofillMagnification = 0.5;
-        break;
-      case 'autofill':
-      case 'autofill-md':
-        break;
-    }
-
-    return `${GRID_DEFAULT_TRACK_WIDTH * autofillMagnification}px`;
-  }
-
-  /**
-   * return auto-calculated grid height
-   * rules:
-   *  1. when height option is set, return it
-   *  2. otherwise, the same value to the width will be returned
-   */
-
-  getOptGridHeight() {
-    const { height } = this.options;
-
-    // when height is specified
-    if (height != null) {
-      return height;
-    }
-
-    // return the value which is same to width
-    return this.getOptGridWidth();
-  }
-
-  /**
-   * return absolute path for the specified path
-   *
-   * @param {string} relativePath relative path from `this.fromPagePath`
-   */
-  getAbsolutePathFor(relativePath) {
-    return decodeURIComponent(
-      pathUtils.normalizePath( // normalize like /foo/bar
-        url.resolve(pathUtils.addTrailingSlash(this.fromPagePath), relativePath),
-      ),
-    );
-  }
-
-}

+ 0 - 21
packages/plugin-attachment-refs/src/client/js/util/TagCacheManagerFactory.js

@@ -1,21 +0,0 @@
-import { TagCacheManager } from '@growi/core';
-
-const STATE_CACHE_NS = 'refs-state-cache';
-
-let _instance;
-export default class TagCacheManagerFactory {
-
-  static getInstance() {
-    if (_instance == null) {
-      // create generateCacheKey implementation
-      const generateCacheKey = (refsContext) => {
-        return `${refsContext.method}__${refsContext.fromPagePath}__${refsContext.args}`;
-      };
-
-      _instance = new TagCacheManager(STATE_CACHE_NS, generateCacheKey);
-    }
-
-    return _instance;
-  }
-
-}

+ 0 - 11
packages/plugin-attachment-refs/src/index.js

@@ -1,11 +0,0 @@
-const isProd = process.env.NODE_ENV === 'production';
-
-module.exports = {
-  pluginSchemaVersion: 4,
-  serverEntries: [
-    isProd ? 'dist/cjs/server-entry.js' : 'src/server-entry.js',
-  ],
-  clientEntries: [
-    'src/client-entry.js',
-  ],
-};

+ 0 - 4
packages/plugin-attachment-refs/src/server-entry.js

@@ -1,4 +0,0 @@
-module.exports = (crowi, app) => {
-  // add routes
-  require('./server/routes')(crowi, app);
-};

+ 0 - 4
packages/plugin-attachment-refs/src/server/routes/index.js

@@ -1,4 +0,0 @@
-module.exports = (crowi, app) => {
-  // add routes
-  app.use('/_api/plugin', require('./refs')(crowi, app));
-};

+ 0 - 11
packages/plugin-attachment-refs/src/utils/logger/index.ts

@@ -1,11 +0,0 @@
-import Logger from 'bunyan';
-import { createLogger } from 'universal-bunyan';
-
-const loggerFactory = function(name: string): Logger {
-  return createLogger({
-    name,
-    config: { default: 'info' },
-  });
-};
-
-export default loggerFactory;

+ 0 - 0
packages/plugin-attachment-refs/.eslintignore → packages/remark-attachment-refs/.eslintignore


+ 0 - 0
packages/plugin-attachment-refs/.gitignore → packages/remark-attachment-refs/.gitignore


+ 1 - 1
packages/plugin-attachment-refs/README.md → packages/remark-attachment-refs/README.md

@@ -1,4 +1,4 @@
-# growi-plugin-attachment-refs
+# remark-attachment-refs
 
 
 Examples
 Examples
 -------
 -------

+ 30 - 0
packages/remark-attachment-refs/client.txt

@@ -0,0 +1,30 @@
+// import { defineConfig } from 'vite';
+// import dts from 'vite-plugin-dts';
+
+// // https://vitejs.dev/config/
+// export default defineConfig({
+//   plugins: [
+//     dts(),
+//   ],
+//   build: {
+//     outDir: 'dist/client',
+//     sourcemap: true,
+//     lib: {
+//       entry: {
+//         index: 'src/client/index.ts',
+//       },
+//       name: 'remark-attachment-refs-libs',
+//       formats: ['es'],
+//     },
+//     rollupOptions: {
+//       external: [
+//         'bunyan',
+//         'http-errors',
+//         'universal-bunyan',
+//         'react',
+//         'react-dom',
+//         /^@growi\/.*/,
+//       ],
+//     },
+//   },
+// });

+ 5 - 3
packages/plugin-attachment-refs/package.json → packages/remark-attachment-refs/package.json

@@ -1,5 +1,5 @@
 {
 {
-  "name": "@growi/plugin-attachment-refs",
+  "name": "@growi/remark-attachment-refs",
   "version": "6.0.0-RC.9",
   "version": "6.0.0-RC.9",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "license": "MIT",
@@ -14,9 +14,11 @@
     "dist"
     "dist"
   ],
   ],
   "scripts": {
   "scripts": {
-    "build": "vite build",
+    "build": "run-p build:*",
+    "build:server": "vite build -c vite.server.config.ts",
     "clean": "npx -y shx rm -rf dist",
     "clean": "npx -y shx rm -rf dist",
-    "dev": "vite build --mode dev",
+    "dev": "run-p dev:*",
+    "dev:server": "vite build -c vite.server.config.ts --mode dev",
     "watch": "yarn dev -w --emptyOutDir=false",
     "watch": "yarn dev -w --emptyOutDir=false",
     "lint:js": "yarn eslint **/*.{js,jsx,ts,tsx}",
     "lint:js": "yarn eslint **/*.{js,jsx,ts,tsx}",
     "lint:styles": "stylelint src/**/*.scss src/**/*.css",
     "lint:styles": "stylelint src/**/*.scss src/**/*.css",

+ 17 - 0
packages/remark-attachment-refs/src/server/index.ts

@@ -0,0 +1,17 @@
+import { routesFactory } from './routes/refs';
+
+const loginRequiredFallback = (req, res) => {
+  return res.status(403).send('login required');
+};
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
+const middleware = (crowi: any, app: any): void => {
+  const refs = routesFactory(crowi);
+
+  const loginRequired = crowi.require('../middlewares/login-required')(crowi, true, loginRequiredFallback);
+  const accessTokenParser = crowi.require('../middlewares/access-token-parser')(crowi);
+
+  app.get('/_api', accessTokenParser, loginRequired, refs);
+};
+
+export default middleware;

+ 14 - 11
packages/plugin-attachment-refs/src/server/routes/refs.js → packages/remark-attachment-refs/src/server/routes/refs.ts

@@ -1,16 +1,16 @@
-import loggerFactory from '../../utils/logger';
+import { OptionParser } from '@growi/core';
 
 
-const { OptionParser } = require('@growi/core');
+// import loggerFactory from '../../utils/logger';
 
 
-const logger = loggerFactory('growi-plugin:attachment-refs:routes:refs');
+// const logger = loggerFactory('growi-plugin:attachment-refs:routes:refs');
 
 
 
 
 const loginRequiredFallback = (req, res) => {
 const loginRequiredFallback = (req, res) => {
   return res.status(403).send('login required');
   return res.status(403).send('login required');
 };
 };
 
 
-
-module.exports = (crowi) => {
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const routesFactory = (crowi): any => {
   const express = crowi.require('express');
   const express = crowi.require('express');
   const mongoose = crowi.require('mongoose');
   const mongoose = crowi.require('mongoose');
 
 
@@ -57,6 +57,11 @@ module.exports = (crowi) => {
     }
     }
 
 
     const range = OptionParser.parseRange(optionsDepth);
     const range = OptionParser.parseRange(optionsDepth);
+
+    if (range == null) {
+      return query;
+    }
+
     const start = range.start;
     const start = range.start;
     const end = range.end;
     const end = range.end;
 
 
@@ -80,8 +85,6 @@ module.exports = (crowi) => {
   router.get('/ref', accessTokenParser, loginRequired, async(req, res) => {
   router.get('/ref', accessTokenParser, loginRequired, async(req, res) => {
     const user = req.user;
     const user = req.user;
     const { pagePath, fileNameOrId } = req.query;
     const { pagePath, fileNameOrId } = req.query;
-    // eslint-disable-next-line no-unused-vars
-    const options = JSON.parse(req.query.options);
 
 
     if (pagePath == null) {
     if (pagePath == null) {
       res.status(400).send('the param \'pagePath\' must be set.');
       res.status(400).send('the param \'pagePath\' must be set.');
@@ -97,7 +100,7 @@ module.exports = (crowi) => {
     }
     }
 
 
     // convert ObjectId
     // convert ObjectId
-    const orConditions = [{ originalName: fileNameOrId }];
+    const orConditions: any[] = [{ originalName: fileNameOrId }];
     if (ObjectId.isValid(fileNameOrId)) {
     if (ObjectId.isValid(fileNameOrId)) {
       orConditions.push({ _id: ObjectId(fileNameOrId) });
       orConditions.push({ _id: ObjectId(fileNameOrId) });
     }
     }
@@ -115,12 +118,12 @@ module.exports = (crowi) => {
       return;
       return;
     }
     }
 
 
-    logger.debug(`attachment '${attachment.id}' is found from fileNameOrId '${fileNameOrId}'`);
+    // logger.debug(`attachment '${attachment.id}' is found from fileNameOrId '${fileNameOrId}'`);
 
 
     // forbidden
     // forbidden
     const isAccessible = await Page.isAccessiblePageByViewer(attachment.page, user);
     const isAccessible = await Page.isAccessiblePageByViewer(attachment.page, user);
     if (!isAccessible) {
     if (!isAccessible) {
-      logger.debug(`attachment '${attachment.id}' is forbidden for user '${user && user.username}'`);
+      // logger.debug(`attachment '${attachment.id}' is forbidden for user '${user && user.username}'`);
       res.status(403).send(`page '${attachment.page}' is forbidden.`);
       res.status(403).send(`page '${attachment.page}' is forbidden.`);
       return;
       return;
     }
     }
@@ -188,7 +191,7 @@ module.exports = (crowi) => {
     const results = await pageQuery.select('id').exec();
     const results = await pageQuery.select('id').exec();
     const pageIds = results.map(result => result.id);
     const pageIds = results.map(result => result.id);
 
 
-    logger.debug('retrieve attachments for pages:', pageIds);
+    // logger.debug('retrieve attachments for pages:', pageIds);
 
 
     // create query to find
     // create query to find
     let query = Attachment
     let query = Attachment

+ 0 - 0
packages/plugin-attachment-refs/tsconfig.json → packages/remark-attachment-refs/tsconfig.json


+ 8 - 8
packages/plugin-attachment-refs/vite.config.ts → packages/remark-attachment-refs/vite.server.config.ts

@@ -1,6 +1,3 @@
-import path from 'path';
-
-import glob from 'glob';
 import { defineConfig } from 'vite';
 import { defineConfig } from 'vite';
 import dts from 'vite-plugin-dts';
 import dts from 'vite-plugin-dts';
 
 
@@ -10,17 +7,19 @@ export default defineConfig({
     dts(),
     dts(),
   ],
   ],
   build: {
   build: {
-    outDir: 'dist',
+    outDir: 'dist/server',
     sourcemap: true,
     sourcemap: true,
     lib: {
     lib: {
-      entry: glob.sync(path.resolve(__dirname, 'src/**/*.{js,jsx}')),
-      name: 'plugin-attachment-refs-libs',
-      formats: ['es', 'cjs'],
+      entry: [
+        'src/server/index.ts',
+      ],
+      name: 'remark-attachment-refs-libs',
+      formats: ['cjs'],
     },
     },
     rollupOptions: {
     rollupOptions: {
       output: {
       output: {
         preserveModules: true,
         preserveModules: true,
-        preserveModulesRoot: 'src',
+        preserveModulesRoot: 'src/server',
       },
       },
       external: [
       external: [
         'bunyan',
         'bunyan',
@@ -28,6 +27,7 @@ export default defineConfig({
         'universal-bunyan',
         'universal-bunyan',
         'react',
         'react',
         'react-dom',
         'react-dom',
+        /^@growi\/.*/,
       ],
       ],
     },
     },
   },
   },

+ 6 - 6
turbo.json

@@ -19,8 +19,8 @@
       "cache": false
       "cache": false
     },
     },
 
 
-    "@growi/plugin-attachment-refs#build": {
-      "dependsOn": ["@growi/core#build"],
+    "@growi/remark-attachment-refs#build": {
+      "dependsOn": ["^build"],
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "outputMode": "new-only"
       "outputMode": "new-only"
     },
     },
@@ -63,8 +63,8 @@
       "outputMode": "new-only"
       "outputMode": "new-only"
     },
     },
 
 
-    "@growi/plugin-attachment-refs#dev": {
-      "dependsOn": ["@growi/core#dev", "@growi/ui#dev"],
+    "@growi/remark-attachment-refs#dev": {
+      "dependsOn": ["^dev"],
       "outputs": ["dist/**"],
       "outputs": ["dist/**"],
       "outputMode": "new-only"
       "outputMode": "new-only"
     },
     },
@@ -132,8 +132,8 @@
       "persistent": true
       "persistent": true
     },
     },
 
 
-    "@growi/plugin-attachment-refs#lint": {
-      "dependsOn": ["@growi/core#lint"]
+    "@growi/remark-attachment-refs#lint": {
+      "dependsOn": ["^dev"]
     },
     },
     "@growi/ui#lint": {
     "@growi/ui#lint": {
       "dependsOn": ["@growi/core#dev"]
       "dependsOn": ["@growi/core#dev"]