├── .ghenvrc.json ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── app ├── root.ts ├── routes │ ├── index.tsx │ └── nested │ │ ├── nestedRoute.tsx │ │ └── testSass.scss └── styles │ └── test.css ├── cli.test.ts ├── cli.ts ├── jest.config.ts ├── package.json ├── remix.config.js ├── tsconfig.json └── yarn.lock /.ghenvrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": "neighbaa/.ghenv", 3 | "pattern": ["**/.env","**/lib/**",".archive/**"] 4 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: '🐛 Bug report' 2 | description: Create a report to help us improve 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Thank you for reporting an issue :pray:. 8 | 9 | `remix-generate-css-links` (https://github.com/neighbaa/remix-generate-css-links). 10 | If you have a question about how to achieve something and are struggling, please post a question 11 | in the `remix-generate-css-links` Discussions tab: https://github.com/neighbaa/remix-generate-css-links/discussions 12 | 13 | Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already: 14 | - `remix-generate-css-links` Issues tab: https://github.com/neighbaa/remix-generate-css-links/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc 15 | - `remix-generate-css-links` Closed Issues tab: https://github.com/neighbaa/remix-generate-css-links/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed 16 | - `remix-generate-css-links` Discussions tab: https://github.com/neighbaa/remix-generate-css-links/discussions 17 | 18 | The more information you fill in, the better the community can help you. 19 | - type: textarea 20 | id: description 21 | attributes: 22 | label: Describe the bug 23 | description: Provide a clear and concise description of the challenge you are running into. 24 | validations: 25 | required: true 26 | - type: input 27 | id: link 28 | attributes: 29 | label: Your Example Website or App 30 | description: | 31 | Which website or app were you using when the bug happened? 32 | Note: 33 | - `remix-generate-css-links` npm package. 34 | - To create a shareable code example you can use Stackblitz (https://stackblitz.com/). Please no localhost URLs. 35 | - Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve. 36 | placeholder: | 37 | e.g. https://stackblitz.com/edit/...... OR Github Repo 38 | validations: 39 | required: true 40 | - type: textarea 41 | id: steps 42 | attributes: 43 | label: Steps to Reproduce the Bug or Issue 44 | description: Describe the steps we have to take to reproduce the behavior. 45 | placeholder: | 46 | 1. Go to '...' 47 | 2. Click on '....' 48 | 3. Scroll down to '....' 49 | 4. See error 50 | validations: 51 | required: true 52 | - type: textarea 53 | id: expected 54 | attributes: 55 | label: Expected behavior 56 | description: Provide a clear and concise description of what you expected to happen. 57 | placeholder: | 58 | As a user, I expected ___ behavior, but i am seeing ___ 59 | validations: 60 | required: true 61 | - type: textarea 62 | id: screenshots_or_videos 63 | attributes: 64 | label: Screenshots or Videos 65 | description: | 66 | If applicable, add screenshots or a video to help explain your problem. 67 | For more information on the supported file image/file types and the file size limits, please refer 68 | to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files 69 | placeholder: | 70 | You can drag your video or image files inside of this editor ↓ 71 | - type: textarea 72 | id: platform 73 | attributes: 74 | label: Platform 75 | value: | 76 | - OS: [e.g. macOS, Windows, Linux] 77 | - Browser: [e.g. Chrome, Safari, Firefox] 78 | - Version: [e.g. 91.1] 79 | validations: 80 | required: true 81 | - type: textarea 82 | id: additional 83 | attributes: 84 | label: Additional context 85 | description: Add any other context about the problem here. 86 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🤔 Feature Requests & Questions 4 | url: https://github.com/neighbaa/remix-generate-css-links/discussions 5 | about: Please ask and answer questions here. 6 | - name: 💬 Remix Discord Channel 7 | url: https://rmx.as/discord 8 | about: Interact with other people using Remix 📀 9 | - name: 💬 New Updates (Twitter) 10 | url: https://twitter.com/remix_run 11 | about: Stay up to date with Remix news on twitter 12 | - name: 🍿 Remix YouTube Channel 13 | url: https://rmx.as/youtube 14 | about: Are you a techlead or wanting to learn more about Remix in depth? Checkout the Remix YouTube Channel -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | 4 | /.cache 5 | /build 6 | /public/build 7 | /coverage 8 | .env 9 | .archive 10 | 11 | yarn-error.log 12 | 13 | /app/.generated-css-links 14 | /app/.generated-css-links.sass-css 15 | /app/.somewhere-else 16 | /.generated-css-links.sass-css 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Julz Hoben 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # remix-generate-css-links 2 | 3 | `remix-generate-css-links` automatically generates links for your imported .css files. You get the convenience of importing css the way you do in regular React projects and still get the benefits of exporting links in your Remix.run project, the Remix way. 4 | 5 | ![Screenshot](https://user-images.githubusercontent.com/52981032/152796510-0ea4e317-a355-4573-a204-a5c769855722.png) 6 | 7 | ![Screenshot](https://user-images.githubusercontent.com/52981032/152798285-20e00249-3ea1-48fa-b13b-f801db8985f4.png) 8 | ![Screenshot](https://user-images.githubusercontent.com/52981032/152802365-19778b0e-2082-4647-9ee1-7688d589ff01.png) 9 | 10 | ![Screenshot](https://user-images.githubusercontent.com/52981032/152801788-22fb86bc-7296-4df2-b6c4-a9b4d2461864.png) 11 | 12 | 13 | ## Why would I use this? 14 | - You want to use Remix, but you don't love all the options you have for styling, detailed here: https://remix.run/docs/en/v1/guides/styling 15 | - You may find it tedious to track and write all of the css imports and link exports across your Remix components - especially if you have many nested components with independent styles. 16 | - You appreciate the benefits of the Remix link system and you want to preserve those benefits, but you want an easier developer experience. 17 | - You don't necessarily want to use Tailwind, or export the same large single css file on every page. 18 | 19 | ## Installation 20 | 21 | - Using yarn: 22 | 23 | ```bash 24 | $ yarn add --dev remix-generate-css-links 25 | ``` 26 | 27 | - Using npm: 28 | 29 | ```bash 30 | $ npm install --save-dev remix-generate-css-links 31 | ``` 32 | 33 | ## Setup 34 | 35 | `package.json` 36 | 37 | ```json 38 | { 39 | "scripts": { 40 | "build": "remix-generate-css-links && remix build", 41 | "dev": "concurrently \"remix-generate-css-links -w\" \"remix dev\"" 42 | } 43 | } 44 | ``` 45 | 46 | ### Using sass 47 | ```json 48 | { 49 | "scripts": { 50 | "build": "remix-generate-css-links --sass && remix build", 51 | "dev": "concurrently \"remix-generate-css-links -w --sass\" \"remix dev\"" 52 | } 53 | } 54 | ``` 55 | 56 | ## Usage 57 | 58 | `app/components/SomeWickedComponent.tsx` 59 | ```typescript 60 | ... 61 | // Import styles however you want. If you want, you can just import like this for side effects. 62 | import './LocalStyle.css'; 63 | import '../../SomeOtherStyle.css'; 64 | 65 | // If you're using sass with the --sass flag, you would import them from ~/../.sass-css 66 | import "~/../.generated-css-links.sass-css/components/SomeWickedComponent.css" // <-- converted from /components/SomeWickedComponent.scss 67 | 68 | // That's it. There's no need to export these as links like you would below. Though if you did, it would still work. 69 | // export const links = () => { 70 | // return [ 71 | // { 72 | // rel: "stylesheet", 73 | // href: LocalStyle 74 | // } 75 | // ] 76 | // } 77 | 78 | ... 79 | ``` 80 | 81 | `app/routes/some-sweet-as-route` 82 | 83 | - Basic 84 | ```typescript 85 | ... 86 | // Generated files live in the app/.generated-css-links directory, unless you specify another directory using the --outdir / -o flag. 87 | // The directory structure is the same as the app/routes, with the ".generated-links" extension OR ~/.generated-css-links/.generated-links 88 | // So in this file, you would get its generated css links like below: 89 | import { links as _links } from "~/.generated-css-links/routes/some-sweet-as-route.generated-links" 90 | 91 | // Then you can export the links. 92 | export const links = () => [..._links()]; 93 | 94 | // If you want to, you could skip importing and simply export the links directly. 95 | export { links } from "~/.generated-css-links/routes/some-sweet-as-route.generated-links" 96 | 97 | // You can also import css into this route file and it will safely be added to its own exported links 98 | import "SomeSweetAsRouteStyle.css" 99 | ``` 100 | 101 | - Merge links 102 | ```typescript 103 | ... 104 | // Additionally, you can use mergeOtherLinks to merge additional links to the exported route links without duplicating them. 105 | import { mergeOtherLinks } from "~/.generated-css-links/routes/some-sweet-as-route.generated-links" 106 | 107 | export const links: LinksFunction = () => { 108 | return mergeOtherLinks([ 109 | { 110 | rel: "stylesheet", 111 | href: globalStylesUrl 112 | }, 113 | { 114 | rel: "stylesheet", 115 | href: globalMediumStylesUrl, 116 | media: "print, (min-width: 640px)" 117 | }, 118 | { 119 | rel: "stylesheet", 120 | href: globalLargeStylesUrl, 121 | media: "screen and (min-width: 1024px)" 122 | }, 123 | { 124 | rel: "apple-touch-icon", 125 | href: appleTouch 126 | }, 127 | { 128 | rel: "stylesheet", 129 | href: "https://fonts.googleapis.com/css2?family=Days+One&display=swap", 130 | }, 131 | { 132 | rel: "stylesheet", 133 | href: "https://fonts.googleapis.com/css2?family=Heebo:wght@900&display=swap", 134 | } 135 | ]); 136 | }; 137 | 138 | ``` 139 | 140 | ## Command Line Options 141 | 142 | - `-w`: Watch for changes and automatically rebuild. 143 | - `-o`: Change the output directory for the generated files. It will live at `/` 144 | - `--sass`: Compile your scss/sass files into /.sass-css 145 | 146 | ## License 147 | 148 | [MIT](LICENSE) 149 | -------------------------------------------------------------------------------- /app/root.ts: -------------------------------------------------------------------------------- 1 | import "~/styles/test.css" 2 | 3 | export {} -------------------------------------------------------------------------------- /app/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/test.css" 2 | import "css-in-node-modules-test/test.css" 3 | 4 | export default {} -------------------------------------------------------------------------------- /app/routes/nested/nestedRoute.tsx: -------------------------------------------------------------------------------- 1 | import "~/../.generated-css-links.sass-css/routes/nested/testSass.css" 2 | 3 | export default {} -------------------------------------------------------------------------------- /app/routes/nested/testSass.scss: -------------------------------------------------------------------------------- 1 | // from basic example at https://sass-lang.com/guide 2 | $font-stack: Helvetica, sans-serif; 3 | $primary-color: #333; 4 | 5 | body { 6 | font: 100% $font-stack; 7 | color: $primary-color; 8 | } -------------------------------------------------------------------------------- /app/styles/test.css: -------------------------------------------------------------------------------- 1 | .test { 2 | color: #000; 3 | } -------------------------------------------------------------------------------- /cli.test.ts: -------------------------------------------------------------------------------- 1 | import util from 'util'; 2 | import { ensureFileSync, writeFileSync } from 'fs-extra'; 3 | 4 | const exec = util.promisify(require('child_process').exec); 5 | 6 | jest.setTimeout(10000) 7 | 8 | beforeAll(() => { 9 | const cssInNodeModulesPath = "node_modules/css-in-node-modules-test/test.css" 10 | ensureFileSync(cssInNodeModulesPath); 11 | writeFileSync(cssInNodeModulesPath, `.test { color: #000; }`); 12 | }); 13 | 14 | test('cli outputs to /app/.generated-css-links', async () => { 15 | const { stdout, stderr } = await exec('ts-node ./cli'); 16 | 17 | if (stderr) console.error(`error: ${stderr}`); 18 | console.log(stdout); 19 | 20 | expect(!!stdout && stderr).toBe(false); 21 | }) 22 | 23 | test('cli outputs to /app/.somewhere-else', async () => { 24 | const { stdout, stderr } = await exec('ts-node ./cli -o .somewhere-else'); 25 | 26 | if (stderr) console.error(`error: ${stderr}`); 27 | console.log(stdout); 28 | 29 | expect(!!stdout && stderr).toBe(false); 30 | }) 31 | 32 | test('cli outputs to /app/.generated-css-links.sass-css', async () => { 33 | const { stdout, stderr } = await exec('ts-node ./cli --sass'); 34 | 35 | if (stderr) console.error(`error: ${stderr}`); 36 | console.log(stdout); 37 | 38 | expect(!!stdout && stderr).toBe(false); 39 | }) -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('ts-node').register(); 3 | 4 | import util from 'util'; 5 | import meow from 'meow'; 6 | import chokidar from 'chokidar'; 7 | import type { Dirent } from "fs"; 8 | 9 | // const sass = require('sass-embedded'); 10 | const exec = util.promisify(require('child_process').exec); 11 | const commandExists = require('command-exists'); 12 | const dependencyTree = require('dependency-tree'); 13 | const { resolve, join, extname } = require('path'); 14 | const { readdir, writeFile } = require('fs').promises; 15 | const { ensureFile } = require('fs-extra'); 16 | 17 | const projectRoot = process.env.REMIX_ROOT || require("app-root-path") 18 | const remixConfig = require(`${projectRoot}/remix.config`) 19 | 20 | // ensure remixConfig is available at the project root dir 21 | if(!remixConfig) { 22 | console.error("Cannot find remix.config.js. Check that this is a Remix.run project.") 23 | process.exit(1) 24 | } 25 | 26 | let OUTDIR: string = ".generated-css-links" 27 | 28 | const helpText = ` 29 | Usage 30 | $ remix-generate-css-links 31 | Options 32 | --watch, -w Watch for routes changes 33 | --outdir -o / for directory to output links files (default ${OUTDIR}) 34 | `; 35 | 36 | const cli = meow(helpText, { 37 | // importMeta: import.meta, 38 | flags: { 39 | watch: { 40 | type: "boolean", 41 | alias: "w" 42 | }, 43 | outdir: { 44 | type: "string", 45 | alias: "o" 46 | }, 47 | sass: { 48 | type: "boolean" 49 | } 50 | } 51 | }); 52 | 53 | if(typeof cli.flags.outdir === "string" && cli.flags.outdir.length) OUTDIR = cli.flags.outdir 54 | 55 | // determine app directory 56 | const remixAppDirectory = remixConfig.appDirectory || "app" 57 | const appdir = `${projectRoot}/${remixAppDirectory}` 58 | const nodeModulesDir = `${projectRoot}/node_modules/` 59 | const projectRootLength = `${projectRoot}`.length + 1 60 | const appdirLength = appdir.length 61 | const nodeModulesDirLength = nodeModulesDir.length 62 | 63 | let sassCommand = false 64 | let sassOutputDir = "" 65 | let sassOutputDirLength = 0 66 | const checkUseSass = async () => { 67 | if(!cli.flags.sass) return false 68 | try { 69 | sassCommand = await commandExists('sass') 70 | } catch(error) { 71 | console.error(`sass is not an available command - either make sass available from the command line, or remove the --sass option`) 72 | process.exit(0) 73 | } 74 | } 75 | 76 | // include routes/ in filepath 77 | const allStyleLinksInOneFile = async (filepath: string) => { 78 | const reducedList: any = [] 79 | 80 | dependencyTree.toList({ 81 | filename: `${appdir}/${filepath}`, 82 | directory: `${appdir}/`, 83 | tsConfig: `${projectRoot}/tsconfig.json`, 84 | filter: (path: string) => (path.split(".").slice(-1)[0] === "css" || path.indexOf('node_modules') === -1) && ((sassCommand && path.substring(0,sassOutputDirLength) === sassOutputDir) || path.indexOf(OUTDIR) === -1), 85 | }) 86 | .forEach((path: string) => { 87 | if(path.split(".").slice(-1)[0] === "css") { 88 | if(path.substring(0,appdirLength) === appdir) 89 | reducedList.push(`~${path.substring(appdirLength)}`) 90 | else if (path.substring(0,nodeModulesDirLength) === nodeModulesDir) 91 | reducedList.push(path.substring(nodeModulesDirLength)) 92 | else if (sassCommand && path.substring(0,sassOutputDirLength) === sassOutputDir) 93 | reducedList.push(`~/../${path.substring(projectRootLength)}`) 94 | // else throw new Error(`Unexpected path prefix found: ${path}, ${path.substring(0,sassOutputDirLength)}, ${sassOutputDir}, ${path.substring(0,sassOutputDirLength) === sassOutputDir}`) 95 | } 96 | }); 97 | 98 | let data = 99 | `import type { HtmlLinkDescriptor } from "remix"; 100 | ${reducedList.map((path: string, index: number) => `import _${index} from "${path}";`).join("\n")} 101 | 102 | export const links = () => { 103 | const htmlLinkDescriptors: HtmlLinkDescriptor[] = [ 104 | ${reducedList.map((path: string, index: number) => { 105 | return `{ rel: "stylesheet", href: _${index} }` 106 | }).join(",\n ")} 107 | ] 108 | return htmlLinkDescriptors 109 | } 110 | 111 | interface UniqueLinksHrefMap { [id: HtmlLinkDescriptor["href"]]: HtmlLinkDescriptor; } 112 | 113 | export const mergeOtherLinks = (_links: HtmlLinkDescriptor[]) => { 114 | const uniqueLinksHrefMap: UniqueLinksHrefMap = {} 115 | _links.forEach(link => uniqueLinksHrefMap[link.href] = link) 116 | return _links.concat(links().filter(link => !uniqueLinksHrefMap[link.href])) 117 | } 118 | `; 119 | 120 | const generatedFileTarget = `${remixAppDirectory}/${OUTDIR}/${filepath.split(".").slice(0,-1).join(".")}.generated-links.ts` 121 | await ensureFile(generatedFileTarget); 122 | await writeFile(generatedFileTarget, data); 123 | } 124 | 125 | async function processFileTree(dir: string) { 126 | const dirents = await readdir(dir, { withFileTypes: true }); 127 | const subDirs: Dirent[] = [] 128 | // handle files at top before subdirs 129 | await Promise.all(dirents.filter((dirent: Dirent) => { 130 | if(dirent.isDirectory()) { 131 | subDirs.push(dirent) 132 | return false 133 | } 134 | const extension: string = extname(dirent.name) 135 | return [".js",".jsx",".ts",".tsx"].includes(extension) 136 | }).map(async (dirent: Dirent) => { 137 | const fullPath = await resolve(dir, dirent.name) 138 | const pathFromAppDirectory = (fullPath).substring(appdirLength+1) 139 | return await allStyleLinksInOneFile(pathFromAppDirectory) 140 | })); 141 | // handle subdirs 142 | await Promise.all(subDirs.map(async (dirent: Dirent) => await processFileTree(await resolve(dir, dirent.name)))) 143 | } 144 | 145 | const debounce = (callback: Function, wait: number) => { 146 | let timeout: any = null 147 | return (...args: any) => { 148 | const next = () => callback(...args) 149 | clearTimeout(timeout) 150 | timeout = setTimeout(next, wait) 151 | } 152 | } 153 | 154 | const checkAndRunSassCommand = async (watching?: boolean) => { 155 | if(sassCommand) { 156 | sassOutputDir = `${projectRoot}/${OUTDIR}.sass-css/` 157 | sassOutputDirLength = sassOutputDir.length 158 | const { stdout, stderr } = await exec(`sass ${watching ? "--watch " : " "}${remixAppDirectory}/:${OUTDIR}.sass-css/`); 159 | } 160 | } 161 | 162 | const build = async (watching?: boolean) => { 163 | if(!watching) { 164 | await checkAndRunSassCommand() 165 | } 166 | 167 | const appRootfilename = require.resolve(resolve(appdir, "root")).substring(appdirLength+1) 168 | await allStyleLinksInOneFile(appRootfilename) 169 | await processFileTree(`${appdir}/routes`) 170 | } 171 | 172 | const debouncedBuild = debounce(build, 200) 173 | 174 | function watch() { 175 | checkAndRunSassCommand(true) 176 | debouncedBuild(true); 177 | const projectRoutes = join(`${projectRoot}`, `${remixAppDirectory}/routes/**/*.{js,jsx,ts,tsx}`) 178 | const projectConfig = join(`${projectRoot}`, 'remix.config.js') 179 | chokidar.watch([projectRoutes, projectConfig]).on('change', () => { 180 | debouncedBuild(true); 181 | }); 182 | console.log('Watching for changes in your app routes...'); 183 | } 184 | 185 | if (require.main === module) { 186 | (async function () { await checkUseSass(); await (cli.flags.watch ? watch : debouncedBuild )() })(); 187 | } 188 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | import type { Config } from '@jest/types'; 3 | import { defaults } from 'jest-config'; 4 | 5 | const config: Config.InitialOptions = { 6 | preset: 'ts-jest', 7 | testEnvironment: 'node', 8 | verbose: true, 9 | detectOpenHandles: true, 10 | moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'], 11 | }; 12 | export default config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remix-generate-css-links", 3 | "version": "0.3.3", 4 | "description": "In your Remix.run project, automatically generate links for your imported .css files. You can import css the way you do in regular React projects and still get the benefits of exporting links in your Remix project the Remix way.", 5 | "main": "lib/index.js", 6 | "types": "index.d.ts", 7 | "bin": { 8 | "remix-generate-css-links": "lib/cli.js" 9 | }, 10 | "files": [ 11 | "lib", 12 | "index.d.ts" 13 | ], 14 | "scripts": { 15 | "watch": "tsc --watch", 16 | "prebuild": "rm -rf lib", 17 | "build": "tsc", 18 | "prepack": "npm run build", 19 | "test": "jest --config=jest.config.ts --passWithNoTests --colors", 20 | "semantic-release": "dotenv semantic-release" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/neighbaa/remix-generate-css-links.git" 25 | }, 26 | "keywords": [ 27 | "remix", 28 | "remix.run", 29 | "generate", 30 | "css", 31 | "links" 32 | ], 33 | "author": "Julian Hoben", 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/neighbaa/remix-generate-css-links/issues" 37 | }, 38 | "homepage": "https://github.com/neighbaa/remix-generate-css-links#readme", 39 | "dependencies": { 40 | "@types/jest": "^27.4.0", 41 | "app-root-path": "^3.0.0", 42 | "chokidar": "^3.5.3", 43 | "command-exists": "^1.2.9", 44 | "dependency-tree": "^8.1.2", 45 | "fs-extra": "^10.0.0", 46 | "meow": "9.0.0", 47 | "sass": "^1.49.7" 48 | }, 49 | "devDependencies": { 50 | "@types/fs-extra": "^9.0.13", 51 | "@types/node": "^17.0.15", 52 | "concurrently": "^6.x", 53 | "cz-conventional-changelog": "3.3.0", 54 | "dotenv-cli": "^5.0.0", 55 | "jest": "^27.5.1", 56 | "remix": "^1.1.3", 57 | "semantic-release": "^19.0.2", 58 | "ts-jest": "^27.1.3", 59 | "ts-node": "^10.4.0", 60 | "typescript": "^4.5.5" 61 | }, 62 | "config": { 63 | "commitizen": { 64 | "path": "./node_modules/cz-conventional-changelog" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /remix.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('@remix-run/dev/config').AppConfig} 3 | */ 4 | module.exports = { 5 | appDirectory: "app", 6 | assetsBuildDirectory: "public/build", 7 | publicPath: "/build/", 8 | serverBuildDirectory: "build", 9 | devServerPort: 8002, 10 | ignoredRouteFiles: [".*"] 11 | }; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["*.ts","**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "outDir": "lib", 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "jsx": "react-jsx", 10 | "module":"CommonJS", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "target": "ES2019", 14 | "strict": true, 15 | "baseUrl": ".", 16 | "paths": { 17 | "~/*": ["./app/*"] 18 | } 19 | } 20 | } 21 | --------------------------------------------------------------------------------